Geschrieben von: Christoph Wille
Kategorie: Sicherheit
This printed page brought to you by AlphaSierraPapa
Im Zuge der Terrorangriffe auf das World Trade Center ist - wieder einmal - eine Debatte zur Rolle von Verschlüsselungstechnologien in der Kommunikation von Terroristen ins Laufen gekommen. Hauptaugenmerk wird in solchen Debatten immer auf sogenannte Hintertüren (Backdoors) für die Regierungsbehörden in Verschlüsselungsalgorithmen gelegt - also, wie Behörden ohne Zustimmung der Kommunizierenden den (verschlüsselten) Datenverkehr abhören können.
Es gibt verschiedenste Ansätze soetwas zu realisieren - dies reicht von klassischen Hintertüren bis hin zu Key Escrow (Hinterlegung von Schlüsseln bei Regierungsbehörden). Allerdings entbehrt die gesamte Diskussion nicht einer gewissen Lächerlichkeit: es gibt nämlich einen unknackbaren Verschlüsselungsalgorithmus, den Vernam Algorithmus, besser bekannt unter dem Namen One-time Pad, der speziell unter Spionen beliebt war oder ist. Jedem, der nur ein wenig von Verschlüsselungstechnik versteht, wird dessen mathematisch bewiesene Unknackbarkeit bekannt sein - also auch Terroristen, denen Tätigkeiten der NSA, Carnivore und Echelon sicherlich ebensowenig unbekannt sein werden wie dem durchschnittlichen Netzwerkadministrator.
Das Ziel des Artikels ist zu zeigen, wie lächerlich einfach und mit wie wenig Code man den Vernam Algorithmus in .NET implementieren kann (im engeren Sinne der Implementierung habe ich weniger als hundert Zeilen benötigt). Was damit auch bewiesen ist, daß unknackbare Verschlüsselung in der Reichweite von jedermann ist, inklusive von Leuten, denen man Verschlüsselung eigentlich verbieten möchte (und die gesamten vorgeschlagenen Einschränkungen die Falschen treffen werden).
Der Vernam Algorithmus ist in seiner Einfachheit absolut bestechend - man benötigt zur Verschlüsselung einer Datei A nur das sogenannte Onetime Pad, welches ebenfalls eine Datei ist. Dieses One-time Pad zeichnet sich durch einige wichtige Eigenschaften aus: es darf nur dem Sender und Empfänger der Nachricht bekannt sein, es sollte mittels eines Pseudo-Zufallszahlengenerator (PRNG - pseudo-random number generator) mit Daten befüllt worden sein, und es dürfen keine Sequenzen des Pads jemals zum Verschlüsseln doppelt verwendet werden.
Wie funktioniert das oben erwähnte Verschlüsseln? Nun, ganz einfach: ein Byte der Ausgangsdatei wird mit je einem Byte des Pads per XOR verschlüsselt. Dann kommt das nächste Byte der Ausgangsdatei als auch des Pads an die Reihe. Jemand, der das Pad nicht in Händen hat, hat nicht die geringste Chance die Nachricht zu entschlüsseln. Für den Empfänger hingegen ist es ein Leichtes.
Der einzige wirkliche Knackpunkt des Vernam Algorithmus ist der Austausch des One-time Pads zwischen Sender und Empfänger. Klarerweise sollte das Pad nicht per Email verschickt werden, aber eine CD per Post dürfte nicht auffällig sein. Vor allem, wenn viele MP3 Dateien darauf sind (als Beispiel), und eine davon statt Musik ein Onetime Pad ist. Ich habe hier nicht vor, eine vollständige Anleitung zu geben.
Wichtig ist, daß nach Verwendung des Pads dasselbe vernichtet werden muß. Liegt das Pad auf der Festplatte, muß die Datei sicher gelöscht (gewiped) werden. Ist es eine CD, reicht gründliches Zerschneiden derselben aus.
Wie bereits bei der Beschreibung des Algorithmus erwähnt, soll das Onetime Pad wirklich zufällig mit Zahlen gefüllt sein, und hier bieten sich PRNG's (pseudo-random number generators) an. Natürlich kann man selbst einen solchen entwickeln, aber warum sich selbst die Arbeit machen? Es gibt einige, darunter zum Beispiel Yarrow von Bruce Schneier und John Kelsey.
Ich war aber noch eine Spur fauler. Im WIN32 API gibt es die Funktion CryptGenRandom, die mir kryptographisch sichere Zufallszahlen liefert. Und im .NET Framework findet sich der RNGCryptoServiceProvider, der eben diesen Service in .NET Applikationen zur Verfügung stellt. Also brauche ich diese Klasse nur zu verwenden, um mir meine Funktion zu schreiben, die ein Onetime Pad einer bestimmten Größe erzeugt (otp.cs):
using System; using System.IO; using System.Security.Cryptography; class Onetimepad { public bool Generate(string strFilename, long nSize) { if (File.Exists(strFilename)) { throw new ArgumentException("OTP file must not exist"); } FileStream theStream = File.Create(strFilename); int nGenerateAtOnce = 1000; int nWriteNow = nGenerateAtOnce; byte[] abStrongRBytes = new Byte[nGenerateAtOnce]; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); for (long nStart=0; nStart <= nSize; nStart += nGenerateAtOnce) { rng.GetBytes(abStrongRBytes); if ((nStart + nGenerateAtOnce) > nSize) nWriteNow = Convert.ToInt32(nSize - nStart); theStream.Write(abStrongRBytes, 0, nWriteNow); } theStream.Close(); return true; }
Damit kann ich ein Onetime Pad anlegen. Um die Verwendung zu erleichtern, habe ich eine Kommandozeilenapplikation erstellt, die diese Funktion wrapt:
padgen.exe padfilename padfilesize
Der Sourcecode findet sich im Download zum Artikel in der Datei padgen.cs. Nach dem Aufruf dieser Applikation habe ich ein kryptographisch sicheres Onetime Pad in der Hand, in der Größe, die ich benötige. Mit dieser Datei kann ich nun bereits verschlüsseln.
Das Verschlüsseln ist im Prinzip ein einfacher XOR Algorithmus, die Implementierung entsprechend nicht besonders aufwendig. Ich habe in die Methode XorFileWithPad bereits etwas Fehlerbehandlungscode eingebaut, wichtiger hingegen sind die Parameter, hier vor allem nPadStartPos. Damit gebe ich an, wo im Pad er anfangen soll, Bytes zum verschlüsseln auszulesen. Dies ist vor allem dann interessant, wenn das Pad groß im Vergleich zur Ausgangsdatei ist, und das Pad für mehrere Verschlüsselungen verwendet werden soll. Entsprechend liefert die Funktion auch das letzte Byte, das zur Verschlüsselung verwendet wurde. Beim nächsten Aufruf sollte man dann an dieser Position im Pad weitermachen (+1 klarerweise).
public long XorFileWithPad(string strInputFile, string strDestinationFile, string strPad, long nPadStartPos) { if (! File.Exists(strPad)) { throw new ArgumentException("OTP file does not exist"); } if (! File.Exists(strInputFile)) { throw new ArgumentException("Input file does not exist"); } if (File.Exists(strDestinationFile)) { throw new ArgumentException("Destination file must not exist"); } FileInfo infoPad = new FileInfo(strPad); FileInfo infoInputFile = new FileInfo(strInputFile); long nInputFileLength = infoInputFile.Length; long nPadLength = infoPad.Length; if ((nPadLength - nPadStartPos) < nInputFileLength) { throw new ArgumentException("Pad is not long enough to Xor file!"); } FileStream fsOutput = File.Create(strDestinationFile); FileStream fsPad = File.OpenRead(strPad); FileStream fsInput = File.OpenRead(strInputFile); int nBufferSize = 1000, nInputSize, nPadSize, nXor; byte[] abInput = new Byte[nBufferSize]; byte[] abPad = new Byte[nBufferSize]; byte[] abOutput = new Byte[nBufferSize]; while (0 != (nInputSize = fsInput.Read(abInput, 0, nBufferSize))) { nPadSize = fsPad.Read(abPad, 0, nBufferSize); for (nXor = 0; nXor < nInputSize; nXor++) abOutput[nXor] = Convert.ToByte(abInput[nXor] ^ abPad[nXor]); fsOutput.Write(abOutput, 0, nInputSize); } fsOutput.Close(); fsInput.Close(); fsPad.Close(); // this method returns the last byte used of the onetime pad // never re-use *any* portion of a onetime pad!! return (nPadStartPos + nInputFileLength); }
Die Verschlüsselung ist ein einfaches XOR, der Hauptteil der Funktion ist mit dem Einlesen und Schreiben von Dateien beschäftigt. Auch hier habe ich eine Wrapper-Kommandozeilenapplikation erstellt:
padxor.exe inputfile padfile padstart outputfile
Der Sourcecode findet sich in padxor.cs (wie auch padgen.cs in der Benutzerfreundlichkeit dringend zu verbessern).
Jetzt kann man Verschlüsseln und Entschlüsseln (ja, die Implementierung ist nun komplett!). Hier eine Beispielsbefehlssequenz:
padgen mypad.bin 200000 padxor otp.cs mypad.bin 0 otp.cs.enc.txt padxor otp.cs.enc.txt mypad.bin 0 otp.cs.dec.txt
Die Datei otp.cs.enc.txt schickt man an den Empfänger, dem man vorher mypad.bin auf anderem als elektronischem Wege zukommen ließ. Dieser kann dann otp.cs.enc.txt mittels des Pads wieder entschlüsseln, wie hier gezeigt in otp.cs.dec.txt.
Obwohl unsere Datei jetzt völlig unknackbar verschickt werden kann, hat dieses Versenden der Nachricht einen kleinen Haken - es kann nicht geknackt werden. Das klingt paradox, aber hier der Hintergrund: durch das Verschlüsseln mit Onetime Pads wird die Nachricht selbst beinahe zufällig, und somit kaum komprimierbar - und dies ist ein ziemlich eindeutiges Indiz für die Verwendung eines Onetime Pads. Man könnte also gefragt werden, die Nachricht doch "höflicherweise" zu entschlüsseln.
Um dieser "höflichen" Anfrage zu entgehen gibt es auch wieder einen Trick: man verschlüsselt die Originalnachricht mit Hilfe des Onetime Pads zu Ciphertext1. Diesen Ciphertext1 verschlüsselt man nun wieder per Vernam, allerdings mit einer völlig unverfänglichen Dummynachricht. Das ergibt dann Ciphertext2, welchen man an den Empfänger verschickt, und sofort das Onetime Pad löscht. (eine detaillierte Beschreibung des Wie? und Warum? finden Sie in Bruce Schneier's Buch Applied Cryptography)
Was bringt das? Zwingt man uns Ciphertext2 zu entschlüsseln, verwenden wir Ciphertext1 zur Entschlüsselung: siehe da, bei dieser Entschlüsselung erhalten wir die Dummynachricht! Die Anwendung des Prinzips sieht mit den Kommandozeilenapplikationen wie folgt aus (siehe auch fooleverybody.bat im Download zum Artikel):
padgen mysecret.pad 200000 padxor 2encrypt.txt mysecret.pad 0 ciphertext1.txt padxor ciphertext1.txt dummymessage.txt 0 ciphertext2.txt padxor ciphertext2.txt ciphertext1.txt 0 plaindummy.txt
Bleibt noch die Vernichtung des One-time Pads, wenn es auf der Festplatte liegt. Auch dazu habe ich eine kleine Funktion geschrieben.
Da es eigentlich nicht zum Algorithmus gehört, habe ich kein korrektes Wipen des Onetime Pads implementiert (es sollte sicherheitshalber mehrmals überschrieben werden, am besten wieder mit einem zufälligen Pad). Die fehlenden Teile kann sicher jeder selbst hinzufügen:
// nTimes intentionally not yet implemented public void WipeFile(string strFilename, int nTimes) { if (! File.Exists(strFilename)) { throw new ArgumentException("The file does not exist"); } FileStream fsFile2Wipe = File.OpenWrite(strFilename); long nBytesInFile = fsFile2Wipe.Length; int nBufferSize = 1000, nWritten = 0; int nWriteNow = nBufferSize; byte[] abBuffer = new Byte[nBufferSize]; for (int i=0; i < nBufferSize; i++) abBuffer[i]=0; for (nWritten = 0; nWritten <= nBytesInFile; nWritten += nBufferSize) { if ((nWritten + nBufferSize) > nBytesInFile) nWriteNow = Convert.ToInt32(nBytesInFile - nWritten); fsFile2Wipe.Write(abBuffer, 0, nWriteNow); } fsFile2Wipe.Close(); }
Die Implementierung des Wipens findet sich in der Kommandozeilenapplikation wipe.exe. Damit haben wir alle Funktionen an der Hand, sicher zu verschlüsseln, und alle Spuren zu verwischen.
Mit diesem Artikel hoffe ich gezeigt zu haben, daß unknackbare Verschlüsselung in Griffweite des Programmierers liegt. Wenn ich wirklich Daten im Transit schützen muß, greife ich mit Sicherheit auf Vernam zurück, und nicht auf symmetrische oder Public Key Algorithmen. Das sollte jeder bei Debatten über Überwachungsstaat und Key Escrow bedenken. Apropos: wußten Sie, daß Papier nicht so leicht abgehört werden kann? <g/>
This printed page brought to you by AlphaSierraPapa
Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20010924.zip
Aber bitte mit Rijndael
http:/www.aspheute.com/artikel/20010528.htm
CAPICOM One
http:/www.aspheute.com/artikel/20020115.htm
Passwörter mit SHA1 absichern
http:/www.aspheute.com/artikel/20010330.htm
Passwörter speichern - aber richtig!
http:/www.aspheute.com/artikel/20040105.htm
PGP-Verschlüsselung bei Dateien
http:/www.aspheute.com/artikel/20000920.htm
Ver- und entschlüsseln von Texten mit PGP
http:/www.aspheute.com/artikel/20000921.htm
Applied Cryptography
http://www.counterpane.com/applied.html
Crypto++
http://www.cryptopp.com
Cryptography FAQ
http://www.arithmetica.com/encrypt/crypfaq.html
Cryptome
http://cryptome.org/
CrypTool
http://www.cryptool.com/
One Time Pad Encryption
http://www.vidwest.com/otp/index.htm
Rudolf Abel - legendary soviet spy
http://members.tripod.com/~RUDOLFABEL/
The Crypto Drop Box
http://www.und.nodak.edu/org/crypto/crypto/
The Hollow Nickel Espionage Case - Rudolf Ivanovich Abel - ...
http://www.fbi.gov/fbinbrief/historic/famcases/abel/abel.htm
Vernam cipher explained
http://www.pro-technix.com/information/crypto/pages/vernam_base.html
Yarrow PRNG
http://www.counterpane.com/yarrow.html
©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.