murph hat geschrieben:ich dachte mir, dass man "a.501" auch auf einem unsicherem wege übermitteln kann, da es schwer zu knacken ist, weil das ergebnis ja unkenntlich ist, sprich: der, der das versucht zu encrypten, wird als ergebnis jede menge sch***e erhalten. Das soll das ganze noch ein wenig erschweren, wenn man dann schonmal die datei "a.501" hat.
Wenn man die "a.501" hat, dann hängt es nur noch am Passwort. Wie Python Master schrieb: One Time Pad ist unknackbar wenn man die Pad-Daten nicht in die Finger bekommt. Die müssen auf einem sicheren Weg übertragen werden. Sonst ist das ganze einfach kein One-Time-Pad mehr.
One-Time-Pad sieht so aus:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""One Time Pad."""
from __future__ import division
import os
import sys
from itertools import izip
from mmap import mmap, ACCESS_READ
from optparse import OptionParser
__author__ = "Marc 'BlackJack' Rintsch"
__version__ = '0.0.1'
__date__ = '$Date: 2006-05-27 22:08:57 +0200 (Sat, 27 May 2006) $'
__revision__ = '$Rev: 857 $'
BLOCK_SIZE = 1024 * 1024 # 1 Mib
def write_random_bytes(count, fileobj, block_size=BLOCK_SIZE):
"""Write cryptographically secure random data."""
while count > 0:
fileobj.write(os.urandom(min(block_size, count)))
count -= block_size
def crypt(in_data, out_file, pad_data):
"""Encrypt `in_data` with `pad_data` and write to `out_file`."""
for in_byte, pad_byte in izip(in_data, pad_data):
out_file.write(chr(ord(in_byte) ^ ord(pad_byte)))
def crypt_file(in_name, out_name, pad_name, offset):
"""Encrypt a file.
The three names are paths to the file to be processed, the file where the
processed data should be written to and the file with the one time pad data.
Additionally the `offset` into the one time pad file has to be given. The
return value is the `offset` plus the length of the processed data.
The function maps the unencrypted file and the one time pad data into
memory so both together must fit into virtual memory.
"""
size = os.path.getsize(in_name)
pad_size = os.path.getsize(pad_name)
in_file = open(in_name, 'rb')
out_file = open(out_name, 'wb')
pad_file = open(pad_name, 'rb')
in_data = mmap(in_file.fileno(), size, access=ACCESS_READ)
pad_data = buffer(mmap(pad_file.fileno(), pad_size, access=ACCESS_READ),
offset)
crypt(in_data, out_file, pad_data)
for fileobj in (in_file, out_file, pad_file):
fileobj.close()
return size + offset
def cmd_generate_pad(options, args):
"""Implements the one time pad generation command."""
if len(args) == 0:
out_file = sys.stdout
else:
out_file = open(args[0], 'wb')
write_random_bytes(options.pad_size * 1000000, out_file)
def cmd_crypt(options, args):
"""Implements the crypt command."""
if len(args) != 3:
sys.exit('need in-, out- and pad-filename')
offset = options.offset
print crypt_file(args[0], args[1], args[2], offset)
def main():
"""Main function."""
usage = '%prog [options] command'
parser = OptionParser(usage=usage, version=__version__)
parser.add_option('-s', '--pad-size', type='int', default=1,
metavar='SIZE',
help='generate pad data with SIZE MB (%default)')
parser.add_option('-o', '--offset', type='int', default=0,
help='offset into one time pad data')
commands = { 'generate': cmd_generate_pad,
'crypt': cmd_crypt }
(options, args) = parser.parse_args()
if len(args) == 0:
parser.error('Need at least one command %r' % commands.keys())
try:
command = commands[args[0]]
except KeyError:
parser.error('Unknown command %r' % args[0])
command(options, args[1:])
if __name__ == '__main__':
main()
Das Programm kann One-Time-Pad Zufallsdaten erzeugen und ver- und entschlüsseln. Zwischen ver- und entschlüsseln gibt's eigentlich keinen Unterschied, da beide Operationen symmetrisch sind. Liegt in der Natur der XOR-Operation.
Nehmen wir einmal an, Alice möchte Bob Nachrichten schicken. Als erstes müssen Zufallsdaten erzeugt werden. Alice nennt diese Datei
bob.pad, damit sie weiss, dass sie diese Datei zum Verschlüsseln von Nachrichten an Bob verwenden muss.
Damit werden 650MB Zufallsdaten erzeugt. Das sollte eine Weile reichen. Diese Daten brennt Alice auf eine CD und übergibt Bob diese bei einem persönlichen Treffen.
Dann kann Alice eine Nachricht verschlüssen:
Code: Alles auswählen
alice> onetimepad.py crypt --offset 0 klartext.txt verschluesselt-0.msg bob.pad
42
Damit wird die Datei
klartext.txt verschlüsselt. Der Offset in die Zufallsdaten ist 0, da dies die erste Nachricht ist. Das Programm gibt den Offset plus die Länge der Nachricht wieder aus. Diese Zahl kann als Offset für die nächste Verschlüsselung benutzt werden. Das ist wichtig, damit niemals die gleichen Zufallsdaten zum Verschlüsseln benutzt werden. Die nächste Nachricht würde Alice wie folgt verschlüssen:
Code: Alles auswählen
alice> onetimepad.py crypt --offset 42 nachtrag.txt verschluesselt-42.msg bob.pad
656
Wie man sieht schreibt Alice den Offset auch noch einmal in den Dateinamen der verschlüsselten Nachricht, damit weiss Bob mit welchem Offset er die Datei wieder entschlüsseln muss. Den Nachtrag entschlüsselt Bob also so:
Code: Alles auswählen
bob> onetimepad.py crypt --offset 42 verschluesselt-42.msg von_alice.txt bob.pad
656
Das Programm hat einige Unzulänglichkeiten. Man muss sich für's Verschlüsseln die Offsets merken, es gibt keine Fehlerbehandlung und ich war zu faul ordentlichen Code für das Dateihandling zu schreiben. Deshalb werden die Eingabedatei und die Pad-Datei mit
mmap.mmap() in den virtuellen Speicher abgebildet. Diese beiden Dateien zusammen dürfen folglich nicht grösser sein, als der virtuelle Speicher der einem Prozess zur Verfügung steht und einzeln nicht grösser als 2GB.
Aber es ist ja auch nur als einfaches Beispiel gedacht. Auf jeden Fall kann kein Angreifer eine abgefangene verschlüsselte Nachricht knacken. Die kann man ansonsten völlig ungesichert übertragen, die sind mathematisch unknackbar. Sogar wenn der Angreifer eine Plaintext Nachricht kennt oder sogar selber erzeugen darf, erlaubt ihm die verschlüsselte Nachricht keine Rückschlüsse auf den Inhalt folgender Nachrichten.