Logischer Fehler bei Implementierung des ENIGMA-Algorithmus

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
Anonymus
User
Beiträge: 3
Registriert: Samstag 21. Juni 2014, 11:09

Hallo,

mein Ziel ist es, den Algorithmus der ENIGMA in Python zu implementieren. Soweit läuft es ganz gut. Die Daten werden über eine Konfigurationsdatei übergeben:

Code: Alles auswählen

[Initialisierung]
umkehrwalze = B
walzenlage = I IV III
ringstellung = 16 26 8
steckerverbindungen = AD CN ET FL GI JV KZ PU QY WX
individueller_spruchschlüssel = RTZ
text = Das Oberkommando der Wehrmacht gibt bekannt:
	Aachen ist gerettet.
	Durch gebuendelten Einsatz der Hilfskraefte konnte die Bedrohung abgewendet
	und die Rettung der Stadt gegen achtzehn Uhr sichergestellt werden.
zurechtgebogene nachricht = DAS OBERKOMMANDO DER WEHRMACHT GIBT BEKANNT:
	AACHEN IST GERETTET.
	DURCH GEBUENDELTEN EINSATZ DER HILFSKRAEFTE KONNTE DIE BEDROHUNG ABGEWENDET
	UND DIE RETTUNG DER STADT GEGEN ACHTZEHN UHR SICHERGESTELLT WERDEN.
ergebnis = MM
Diese Konfigurationsdatei wird von meinem Programm validiert und zur Verschlüsselung / Entschlüsselung benutzt. Mein Programm:

Code: Alles auswählen

# Soll in ferner Zukunft Nachrichten der ENIGMA I ver-/entschlüsseln können

from string import ascii_uppercase
import configparser
import re

## ================ 1. Initialisierung ===========================================
c = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
# Sorgt dafür, dass Kommentare nicht mitverarbeitet werden. ACHTUNG:
# Kommentare werden nicht bewahrt, sondern gelöscht
# Dieses Feature wird von configarser, dem Standard-Modul von Python für
# das Parser von Konfigurationsdateien nicht unterstützt

c.read("test.ini")

# == 1.1 Paarung: Erschaffung des Wertes und Überprüfung auf syntaktische
# Korrektheit ==
umkehrwalze = c.get("Initialisierung", "Umkehrwalze")

if re.fullmatch("A|B|C", umkehrwalze) == None:
    raise ValueError("Die Umkehrwalze in der Konfigurationsdatei ist weder A, B, noch C!")
## print(umkehrwalze)


walzenlage = c.get("Initialisierung", "Walzenlage")
walzenlage = walzenlage.split()

if len(walzenlage) == 3 and walzenlage[0] != walzenlage[1] != walzenlage[2]:
    for i in walzenlage:
        if re.fullmatch("I|II|III|IV|V", i) == None:
            print("""Einer der Bestandteile der Walzenlage aus der Konfigura-
tionsdatei ist keine römische Zahl zwischen 1 und 5""")
else:
    print("""Dieses Programm arbeitet nur mit exakt drei Walzen. Sie haben in
der Konfigurationsdatei zu viele oder zu wenige angegeben""")
    

ringstellung = c.get("Initialisierung", "Ringstellung")
ringstellung = ringstellung.split()  # Ringstellung noch nicht implementiert
## print(ringstellung)

steckerung = c.get("Initialisierung", "Steckerverbindungen")
steckerverbindungen = steckerung.split()

if len(steckerverbindungen) <= 10:
    for i in steckerverbindungen:
        if re.fullmatch("[A-Z]{2}", i) == None:
            raise ValueError("Die Steckerverbindungen bestehen nicht aus exakt zwei Großbuchstaben!")
    ## print(steckerverbindungen)


spruchschluessel = c.get("Initialisierung", "individueller_Spruchschlüssel")

if re.fullmatch("[A-Z]{3}", spruchschluessel) == None:
    raise ValueError("""Der Wert des Spruchschlüssels in der Konfigurationsdatei
besteht nicht aus drei Großbuchstaben! """+spruchschluessel)
spruchschluessel = list(spruchschluessel)

#=== 1.2. alle Werte überprüft, Erzeugung der temporären Variablen =============

for l in spruchschluessel:
    spruchschluessel[spruchschluessel.index(l)] = ord(l)-65  # "A" --> 0
print("Spruchschlüssel: ", spruchschluessel)

nachricht = c.get("Initialisierung", "Text")
## print(nachricht)

ascii_uppercase = list(ascii_uppercase)

# Buchstaben-Zahlen-Mapping
zuzahl = dict(zip(list(ascii_uppercase), list(range(26))))
zubuchstabe = dict((value, key) for (key, value) in zuzahl.items())
print("Zuzahl: ", zuzahl)
print("zubuchstabe: ", zubuchstabe)
print("ASCII_uppercase: ", ascii_uppercase)

# interne Verkabelung der Walzen
walzeI = ["E", "K", "M", "F", "L", "G", "D", "Q", "V", "Z", "N", "T", "O", "W",
          "Y", "H", "X", "U", "S", "P", "A", "I", "B", "R", "C", "J"]
walzeII = ["A", "J", "D", "K", "S", "I", "R", "U", "X", "B", "L", "H", "W",
           "T", "M", "C", "Q", "G", "Z", "N", "P", "Y", "F", "V", "O", "E"]
walzeIII = ["B", "D", "F", "H", "J", "L", "C", "P", "R", "T", "X", "V", "Z",
            "N", "Y", "E", "I", "W", "G", "A", "K", "M", "U", "S", "Q", "O"]
walzeIV = ["E", "S", "O", "V", "P", "Z", "J", "A", "Y", "Q", "U", "I", "R",
           "H", "X", "L", "N", "F", "T", "G", "K", "D", "C", "M", "W", "B"]
walzeV = ["V", "Z", "B", "R", "G", "I", "T", "Y", "U", "P", "S", "D", "N",
          "H", "L", "X", "A", "W", "M", "J", "Q", "O", "F", "E", "C", "K"]

print("Walze 3: ", list(zip(ascii_uppercase, walzeIII)))

walze_I = [dict(zip(ascii_uppercase, walzeI)), zuzahl["R"]]
walze_II = [dict(zip(ascii_uppercase, walzeII)), zuzahl["F"]]
walze_III = [dict(zip(ascii_uppercase, walzeIII)), zuzahl["W"]]
walze_IV = [dict(zip(ascii_uppercase, walzeIV)), zuzahl["K"]]
walze_V = [dict(zip(ascii_uppercase, walzeV)), zuzahl["A"]]


#print(walze_V)

for f in walzenlage:
    if f == "I":
        walzenlage[walzenlage.index(f)] = walze_I
    elif f == "II":
        walzenlage[walzenlage.index(f)] = walze_II
    elif f == "III":
        walzenlage[walzenlage.index(f)] = walze_III
    elif f == "IV":
        walzenlage[walzenlage.index(f)] = walze_IV
    elif f == "V":
        walzenlage[walzenlage.index(f)] = walze_V
    else:
        print("Fehler in der Walzenlagenverarbeitung")
print("Walzenlage: ", walzenlage)

walzenlage_invers = []
print("")
for f in walzenlage:
    print("f: ", f[0])
    einzelnes_dict = {wert : schluessel for (schluessel, wert) in f[0].items()}
    print("dict: ", einzelnes_dict)
    walzenlage_invers.append(einzelnes_dict)
walzenlage_invers = tuple(walzenlage_invers)
print("walzenlage_invers: ", walzenlage_invers)

# Steckerung:

print("Steckerverbindungen: ", steckerverbindungen)
#print({i[0]:i[1] for i in steckerverbindungen})
#print({i[1]:i[0] for i in steckerverbindungen})

steckerungsdict = {i[0]:i[1] for i in steckerverbindungen}
#print("Steckerungsdict: ", steckerungsdict)

## print(steckerungsdict)
inverses_steckerungsdict = {value:key for (key,value) in steckerungsdict.items()}
## print(inverses_steckerungsdict)

steckerungsdict.update(inverses_steckerungsdict)  # Die Steckerung ist invers, so aufwendig implementiert, da das sofortige Erzeugen+Zusammenfügen nicht möglich war (Erzeugung eines
                                                  # Objektes des Types NoneType)

##print(steckerungsdict)
##print(len(steckerverbindungen))
##print(len(steckerungsdict))

# Umkehrwalzen

# Beispiel für die Erzeugung des Dictionarys ("Hash" oder "associative array"
# in anderen Programmiersprachen), das die Daten der Umkehrwalze B enthält:
##>>> s = " AY  BR  CU  DH  EQ  FS  GL  IP  JX  KN  MO  TZ  VW "
##>>> e = s.split()
##>>> e
##['AY', 'BR', 'CU', 'DH', 'EQ', 'FS', 'GL', 'IP', 'JX', 'KN', 'MO', 'TZ', 'VW']
##>>> umkb = {i[0]:i[1] for i in e}
##>>> umkb
##{'V': 'W', 'T': 'Z', 'F': 'S', 'G': 'L', 'D': 'H', 'E': 'Q', 'B': 'R', 'C': 'U', 'A': 'Y', 'M': 'O', 'J': 'X', 'K': 'N', 'I': 'P'}
##>>> umkb.update(dict((value, key) for (key, value) in umkb.items())
##>>> len(e)
##13
##>>> len(umkb)
##13


umkehrwalze_A = {'Y': 'G', 'X': 'H', 'Z': 'D', 'Q': 'O', 'P': 'U', 'S': 'T',
                 'R': 'N', 'U': 'P', 'T': 'S', 'W': 'K', 'V': 'I', 'I': 'V',
                 'H': 'X', 'K': 'W', 'J': 'B', 'M': 'C', 'L': 'F', 'O': 'Q',
                 'N': 'R', 'A': 'E', 'C': 'M', 'B': 'J', 'E': 'A', 'D': 'Z',
                 'G': 'Y', 'F': 'L'}
umkehrwalze_B = {'Y': 'A', 'X': 'J', 'Z': 'T', 'Q': 'E', 'P': 'I', 'S': 'F',
                 'R': 'B', 'U': 'C', 'T': 'Z', 'W': 'V', 'V': 'W', 'I': 'P',
                 'H': 'D', 'K': 'N', 'J': 'X', 'M': 'O', 'L': 'G', 'O': 'M',
                 'N': 'K', 'A': 'Y', 'C': 'U', 'B': 'R', 'E': 'Q', 'D': 'H',
                 'G': 'L', 'F': 'S'}
umkehrwalze_C = {'Y': 'H', 'X': 'M', 'Z': 'L', 'Q': 'T', 'P': 'C', 'S': 'U',
                 'R': 'K', 'U': 'S', 'T': 'Q', 'W': 'N', 'V': 'B', 'I': 'E',
                 'H': 'Y', 'K': 'R', 'J': 'D', 'M': 'X', 'L': 'Z', 'O': 'G',
                 'N': 'W', 'A': 'F', 'C': 'P', 'B': 'V', 'E': 'I', 'D': 'J',
                 'G': 'O', 'F': 'A'}

if umkehrwalze == "A":
    verwendete_umkehrwalze = umkehrwalze_A
elif umkehrwalze == "B":
    verwendete_umkehrwalze = umkehrwalze_B
elif umkehrwalze == "C":
    verwendete_umkehrwalze = umkehrwalze_C

# Nachricht nachbearbeiten! ersetzt Kleinbuchstaben und Ziffern durch
# Großbuchstaben und ausgeschrieben Zahlen
nachricht = nachricht.upper()
nachricht = nachricht.replace("Ü", "UE").replace("Ö","OE").replace("Ä","AE")
nachricht_bearbeitet = ""
for i in nachricht:
    if i =="0":
        i="NULL"
        
    elif i == "1":
        i="EINS"
        
    elif i == "2":
        i="ZWEI"
        
    elif i == "3":
        i="DREI"
        
    elif i == "4":
        i="VIER"
        
    elif i == "5":
        i="FUENF"
        
    elif i == "6":
        i="SECHS"
        
    elif i == "7":
        i="SIEBEN"
        
    elif i == "8":
        i="ACHT"
        
    elif i == "9":
        i="NEUN"
        
    else:
        pass
    
    nachricht_bearbeitet += i

c.set("Initialisierung", "zurechtgebogene Nachricht", nachricht_bearbeitet)
print(c.items("Initialisierung"))

nachricht = nachricht_bearbeitet
del nachricht_bearbeitet


## ======================== "ausführender" Programmteil =======================

ergebnis = ""
print(zubuchstabe)

def enigma():
    global ergebnis
    print(verwendete_umkehrwalze)
    print("==========================================================")
    print("zuzahl = ", zuzahl)
    print("\n", "walzenlage[2][1] = ", walzenlage[2][1])
    for i in nachricht:
        if nachricht.index(i) < 2:
            print("===================================================")
            print("Anfang: ", i)
            if i in ascii_uppercase:
                if i in steckerungsdict.keys():
                    i = steckerungsdict[i]
                    print("gesteckert: ", i)
                else:
                    pass
                print("Position der ersten Walze: ", spruchschluessel[2], "/", zubuchstabe[spruchschluessel[2]])
                spruchschluessel[2] = (spruchschluessel[2]+1) % 26 # Fortschalten der ersten Walze bei jedem Tastendruck; Spruchschluessel = Liste der drei Walzenlagen in Zahlen
                print("1. Walze fortgeschaltet")
                print("Position der ersten Walze: ", spruchschluessel[2], "/", zubuchstabe[spruchschluessel[2]])
                if spruchschluessel[2] == walzenlage[2][1]:
                    spruchschluessel[1] = (spruchschluessel[1]+1) % 26
                    print("2. Walze fortgeschaltet")
                    if spruchschluessel[1] == walzenlage[1][1]:
                        spruchschluessel[0] = (spruchschluessel[0]+1) % 26
                        spruchschluessel[1] = (spruchschluessel[1]+1) % 26 # jetzt sind die Walzen fortgeschaltet
                        print("3. und 2. Walze fortgeschaltet")
                # Momentanzustand: i = Großbuchstabe
                # zuzahl = Buchstabe-Zahl-Dict
                # zubuchstabe = Zahl-Buchstabe-Dict
                i = walzenlage[2][0][zubuchstabe[(zuzahl[i]+spruchschluessel[2])%26]] # Walzen
                print("1. Walze: ", i)
                i = walzenlage[1][0][zubuchstabe[(zuzahl[i]+
                                                  spruchschluessel[1]-spruchschluessel[2])%26]]
                print("2. Walze: ", i)
                i = walzenlage[0][0][zubuchstabe[(zuzahl[i]\
                    +spruchschluessel[0]-spruchschluessel[1]\
                    )%26]]
                print("3. Walze: ", i)  # bis hierhin stimmt´s
                #===============================================================
                # print("Walzenlage: ", walzenlage)
                # print("Buchstabe bis hierhin (korrekt): ", i)
                # print("Buchstabe bis hierhin (als Zahl): ", zuzahl[i])
                # print("Spruchschlüssel [0] (als Buchstabe):",
                #       zubuchstabe[spruchschluessel[0]])
                # print("Spruchschlüssel [0] (als Zahl) :", spruchschluessel[0])
                # print("verwendete Umkehrwalze: B / ", verwendete_umkehrwalze)
                # print("inverser Buchstabe: ", (25-zuzahl[i])%26)
                # print("Zwischenergebnis (als Buchstabe): ", zubuchstabe[(zuzahl[i]+spruchschluessel[0])%26])
                # print("Zwischenergebnis (als Zahl): ", (zuzahl[i]+spruchschluessel[0])%26)
                #===============================================================
                print("Umkehrwalze['e']: ", verwendete_umkehrwalze["E"])
                print("umkehrwalze - 3. walze: ", zubuchstabe[(zuzahl[i]-spruchschluessel[0])%26])
                print("wieder ohne Zahl: ", (zuzahl[i]-spruchschluessel[0]))
                i = verwendete_umkehrwalze[zubuchstabe[(zuzahl[i]-spruchschluessel[0])%26]]
                print("Nach Umkehrwalze: ", i)
                print("3. Walze invers['C']: ", walzenlage_invers[0]["C"])
                print("3. Walze invers: ", walzenlage[0][0]["O"])
                print("Zwischenergebnis: ", zuzahl[i])
                i = walzenlage_invers[0][zubuchstabe[(zuzahl[i]+spruchschluessel[0]-spruchschluessel[1]-spruchschluessel[2])%26]]
                print("3. Walze beim zweiten Mal: ", i)
                print("nach der 3. Walze beim zweiten Male (als Zahl): ", zuzahl[i])
                print("zweite Walze: ", walzenlage[1][0])
                i = walzenlage_invers[1][zubuchstabe[(zuzahl[i]+spruchschluessel[0])%26]]
                print("2. Walze beim zweiten Mal: ", i)
                i = walzenlage_invers[2][zubuchstabe[(zuzahl[i]-spruchschluessel[2])%26]]
                print("1. Walze beim zweiten Mal: ", i)
                # sonstiges ...
                if i in steckerungsdict.keys():
                    i = steckerungsdict[i]
                else:
                    pass
                print("zum zweiten Male gesteckert = Ergebnis: ", i)
            ergebnis += i
            print("Ergebnis: ", i)
        else:
            break
    print()
    print(ergebnis)
    c.set("Initialisierung", "Ergebnis", ergebnis)
    file = open("test.ini", "w")
    c.write(file)

if __name__ == "__main__":
    enigma()
Der Algorithmus steckt in der Funktion enigma(), der Rest dient zur Verdeutlichung der Variablenbelegung. Wenn man den Quelltext unter Python 3 ausführt und die Ergebnisse mit http://people.physik.hu-berlin.de/~pall ... 3_v16.html unter den gegebenen Einstellungen mit aktiviertem Steckerbrett und Monitor betrachtet, sieht man, dass der Signalfluss bis zur Umkehrwalze nahezu identisch ist (ETW ist nicht implementiert, in der Shell beginnt die das Problem betreffende Ausgabe ab dem vorletzten Separator). Ab der Umkehrwalze jedoch gehen die Ergebnisse auseinander. Wo liegt mein Fehler?

Danke.

PS: Ich habe auf Klassen etc. verzichtet, da die Dateien für mich einen PoC darstellen, den ich irgendwann nach OpenCL implementieren möchte. Da ich in OpenCL noch sehr unerfahren bin, habe ich mich dazu entschieden, nur möglichst einfache grammatikalische Strukturen zu verwenden, um die Portierung zu erleichtern.
BlackJack

@Anonymus: Du erwartest echt das Dir jemand einen Verschlüsselungsalgorithmus debuggt bei dem Du anscheinend viel Wert auf möglichst schlechten Code gelegt hast weil Du Dich auf ”einfache” Operationen beschränken willst um das irgendwann mal nach OpenCL zu portieren?

Schon mal überlegt das die Fehlersuche problematisch ist gerade weil der Quelltext so furchtbar aussieht? Mit fast zwei Drittel des Programms auf Modulebene und einem Drittel in *einer* viel zu langen Funktion, die dazu noch nicht einmal semantisch eine Funktion ist weil sie keine Argumente bekommt und keinen Rückgabewert hat, dafür aber fröhlich mit globalen Variablen aus den anderen zwei Dritteln des Quelltextes hantiert.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Anonymus hat geschrieben:PS: Ich habe auf Klassen etc. verzichtet, da die Dateien für mich einen PoC darstellen, den ich irgendwann nach OpenCL implementieren möchte.
Ein gewisses Minimum an Struktur würde ich im Programm aber schon erwarten. Aktuell ist das einfach nur ein großer Verhau, bei dem ich nur zu gut verstehe, warum die Fehlersuche schwierig ist. Zumindest eine sinnvolle Aufteilung des Codes in Funktionen würde ich schon erwarten.

Edit: Die Antwort lag zu lange bei mir im Puffer. Eigentlich hat BlackJack schon das Passende gesagt..
Anonymus
User
Beiträge: 3
Registriert: Samstag 21. Juni 2014, 11:09

Gefällt euch diese Version besser?

Code: Alles auswählen

# Soll in ferner Zukunft Nachrichten der ENIGMA I ver-/entschlüsseln können

from string import ascii_uppercase
import configparser
import re

## ================ 1. Initialisierung ===========================================

def initialisierung(datei):
    global ascii_uppercase
    c = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
    # Sorgt dafür, dass Kommentare nicht mitverarbeitet werden. ACHTUNG:
    # Kommentare werden nicht bewahrt, sondern gelöscht
    # Dieses Feature wird von configarser, dem Standard-Modul von Python für
    # das Parser von Konfigurationsdateien nicht unterstützt

    c.read(datei)

    # == 1.1 Paarung: Erschaffung des Wertes und Überprüfung auf syntaktische
    # Korrektheit ==
    umkehrwalze = c.get("Initialisierung", "Umkehrwalze")

    if re.fullmatch("A|B|C", umkehrwalze) == None:
        raise ValueError("Die Umkehrwalze in der Konfigurationsdatei ist weder A, B, noch C!")


    walzenlage = c.get("Initialisierung", "Walzenlage")
    walzenlage = walzenlage.split()

    if len(walzenlage) == 3 and walzenlage[0] != walzenlage[1] != walzenlage[2]:
        for i in walzenlage:
            if re.fullmatch("I|II|III|IV|V", i) == None:
                print("""Einer der Bestandteile der Walzenlage aus der Konfigura-
    tionsdatei ist keine römische Zahl zwischen 1 und 5""")
    else:
        print("""Dieses Programm arbeitet nur mit exakt drei Walzen. Sie haben in
    der Konfigurationsdatei zu viele oder zu wenige angegeben""")
        

    ringstellung = c.get("Initialisierung", "Ringstellung")
    ringstellung = ringstellung.split()  # Ringstellung noch nicht implementiert

    steckerung = c.get("Initialisierung", "Steckerverbindungen")
    steckerverbindungen = steckerung.split()

    if len(steckerverbindungen) <= 10:
        for i in steckerverbindungen:
            if re.fullmatch("[A-Z]{2}", i) == None:
                raise ValueError("Die Steckerverbindungen bestehen nicht aus exakt zwei Großbuchstaben!")

    spruchschluessel = c.get("Initialisierung", "individueller_Spruchschlüssel")

    if re.fullmatch("[A-Z]{3}", spruchschluessel) == None:
        raise ValueError("""Der Wert des Spruchschlüssels in der Konfigurationsdatei
    besteht nicht aus drei Großbuchstaben! """+spruchschluessel)
    spruchschluessel = list(spruchschluessel)

    #=== 1.2. alle Werte überprüft, Erzeugung der temporären Variablen =============

    for l in spruchschluessel:
        spruchschluessel[spruchschluessel.index(l)] = ord(l)-65  # "A" --> 0
    print("Spruchschlüssel: ", spruchschluessel)
    print("----------------------------------------------------------------")

    nachricht = c.get("Initialisierung", "Text")

    ascii_uppercase = list(ascii_uppercase)

    # Buchstaben-Zahlen-Mapping
    zuzahl = dict(zip(list(ascii_uppercase), list(range(26))))
    zubuchstabe = dict((value, key) for (key, value) in zuzahl.items())
    print("Zuzahl: ", zuzahl)
    print("----------------------------------------------------------------")
    print("zubuchstabe: ", zubuchstabe)
    print("----------------------------------------------------------------")
    print("ASCII_uppercase: ", ascii_uppercase)
    print("----------------------------------------------------------------")

    # interne Verkabelung der Walzen
    walzeI = ["E", "K", "M", "F", "L", "G", "D", "Q", "V", "Z", "N", "T", "O", "W",
              "Y", "H", "X", "U", "S", "P", "A", "I", "B", "R", "C", "J"]
    walzeII = ["A", "J", "D", "K", "S", "I", "R", "U", "X", "B", "L", "H", "W",
               "T", "M", "C", "Q", "G", "Z", "N", "P", "Y", "F", "V", "O", "E"]
    walzeIII = ["B", "D", "F", "H", "J", "L", "C", "P", "R", "T", "X", "V", "Z",
                "N", "Y", "E", "I", "W", "G", "A", "K", "M", "U", "S", "Q", "O"]
    walzeIV = ["E", "S", "O", "V", "P", "Z", "J", "A", "Y", "Q", "U", "I", "R",
               "H", "X", "L", "N", "F", "T", "G", "K", "D", "C", "M", "W", "B"]
    walzeV = ["V", "Z", "B", "R", "G", "I", "T", "Y", "U", "P", "S", "D", "N",
              "H", "L", "X", "A", "W", "M", "J", "Q", "O", "F", "E", "C", "K"]

    print("Walze 3: ", list(zip(ascii_uppercase, walzeIII)))
    print("----------------------------------------------------------------")

    walze_I = [dict(zip(ascii_uppercase, walzeI)), zuzahl["R"]]
    walze_II = [dict(zip(ascii_uppercase, walzeII)), zuzahl["F"]]
    walze_III = [dict(zip(ascii_uppercase, walzeIII)), zuzahl["W"]]
    walze_IV = [dict(zip(ascii_uppercase, walzeIV)), zuzahl["K"]]
    walze_V = [dict(zip(ascii_uppercase, walzeV)), zuzahl["A"]]

    for f in walzenlage:
        if f == "I":
            walzenlage[walzenlage.index(f)] = walze_I
        elif f == "II":
            walzenlage[walzenlage.index(f)] = walze_II
        elif f == "III":
            walzenlage[walzenlage.index(f)] = walze_III
        elif f == "IV":
            walzenlage[walzenlage.index(f)] = walze_IV
        elif f == "V":
            walzenlage[walzenlage.index(f)] = walze_V
        else:
            print("Fehler in der Walzenlagenverarbeitung")
            
    print("Walzenlage: ", walzenlage)
    print("----------------------------------------------------------------")

    walzenlage_invers = []
    print("")
    for f in walzenlage:
        print("f: ", f[0])
        einzelnes_dict = {wert : schluessel for (schluessel, wert) in f[0].items()}
        print("dict: ", einzelnes_dict)
        walzenlage_invers.append(einzelnes_dict)

    print("----------------------------------------------------------------")
    walzenlage_invers = tuple(walzenlage_invers)
    print("walzenlage_invers: ", walzenlage_invers)
    print("----------------------------------------------------------------")

    # Steckerung:

    print("Steckerverbindungen: ", steckerverbindungen)
    print("----------------------------------------------------------------")

    steckerungsdict = {i[0]:i[1] for i in steckerverbindungen}

    inverses_steckerungsdict = {value:key for (key,value) in steckerungsdict.items()}

    steckerungsdict.update(inverses_steckerungsdict)  # Die Steckerung ist invers, so aufwendig implementiert, da das sofortige Erzeugen+Zusammenfügen nicht möglich war (Erzeugung eines
                                                      # Objektes des Types NoneType)
                                                      
    # Umkehrwalzen

    # Beispiel für die Erzeugung des Dictionarys ("Hash" oder "associative array"
    # in anderen Programmiersprachen), das die Daten der Umkehrwalze B enthält:
    ##>>> s = " AY  BR  CU  DH  EQ  FS  GL  IP  JX  KN  MO  TZ  VW "
    ##>>> e = s.split()
    ##>>> e
    ##['AY', 'BR', 'CU', 'DH', 'EQ', 'FS', 'GL', 'IP', 'JX', 'KN', 'MO', 'TZ', 'VW']
    ##>>> umkb = {i[0]:i[1] for i in e}
    ##>>> umkb
    ##{'V': 'W', 'T': 'Z', 'F': 'S', 'G': 'L', 'D': 'H', 'E': 'Q', 'B': 'R', 'C': 'U', 'A': 'Y', 'M': 'O', 'J': 'X', 'K': 'N', 'I': 'P'}
    ##>>> umkb.update(dict((value, key) for (key, value) in umkb.items())
    ##>>> len(e)
    ##13
    ##>>> len(umkb)
    ##13


    umkehrwalze_A = {'Y': 'G', 'X': 'H', 'Z': 'D', 'Q': 'O', 'P': 'U', 'S': 'T',
                     'R': 'N', 'U': 'P', 'T': 'S', 'W': 'K', 'V': 'I', 'I': 'V',
                     'H': 'X', 'K': 'W', 'J': 'B', 'M': 'C', 'L': 'F', 'O': 'Q',
                     'N': 'R', 'A': 'E', 'C': 'M', 'B': 'J', 'E': 'A', 'D': 'Z',
                     'G': 'Y', 'F': 'L'}
    umkehrwalze_B = {'Y': 'A', 'X': 'J', 'Z': 'T', 'Q': 'E', 'P': 'I', 'S': 'F',
                     'R': 'B', 'U': 'C', 'T': 'Z', 'W': 'V', 'V': 'W', 'I': 'P',
                     'H': 'D', 'K': 'N', 'J': 'X', 'M': 'O', 'L': 'G', 'O': 'M',
                     'N': 'K', 'A': 'Y', 'C': 'U', 'B': 'R', 'E': 'Q', 'D': 'H',
                     'G': 'L', 'F': 'S'}
    umkehrwalze_C = {'Y': 'H', 'X': 'M', 'Z': 'L', 'Q': 'T', 'P': 'C', 'S': 'U',
                     'R': 'K', 'U': 'S', 'T': 'Q', 'W': 'N', 'V': 'B', 'I': 'E',
                     'H': 'Y', 'K': 'R', 'J': 'D', 'M': 'X', 'L': 'Z', 'O': 'G',
                     'N': 'W', 'A': 'F', 'C': 'P', 'B': 'V', 'E': 'I', 'D': 'J',
                     'G': 'O', 'F': 'A'}

    if umkehrwalze == "A":
        verwendete_umkehrwalze = umkehrwalze_A
    elif umkehrwalze == "B":
        verwendete_umkehrwalze = umkehrwalze_B
    elif umkehrwalze == "C":
        verwendete_umkehrwalze = umkehrwalze_C

    # Nachricht nachbearbeiten! ersetzt Kleinbuchstaben und Ziffern durch
    # Großbuchstaben und ausgeschrieben Zahlen
    nachricht = nachricht.upper()
    nachricht = nachricht.replace("Ü", "UE").replace("Ö","OE").replace("Ä","AE")
    nachricht_bearbeitet = ""
    for i in nachricht:
        if i =="0":
            i="NULL"
            
        elif i == "1":
            i="EINS"
            
        elif i == "2":
            i="ZWEI"
            
        elif i == "3":
            i="DREI"
            
        elif i == "4":
            i="VIER"
            
        elif i == "5":
            i="FUENF"
            
        elif i == "6":
            i="SECHS"
            
        elif i == "7":
            i="SIEBEN"
            
        elif i == "8":
            i="ACHT"
            
        elif i == "9":
            i="NEUN"
            
        else:
            pass
        
        nachricht_bearbeitet += i

    c.set("Initialisierung", "zurechtgebogene Nachricht", nachricht_bearbeitet)
    print(c.items("Initialisierung"))

    nachricht = nachricht_bearbeitet
    del nachricht_bearbeitet
    return datei, c, umkehrwalze, walzenlage, ringstellung, steckerverbindungen, spruchschluessel, steckerungsdict, nachricht, zuzahl, zubuchstabe, walzenlage_invers, verwendete_umkehrwalze


## ======================== "ausführender" Programmteil =======================

ergebnis = ""


def steckern(buchstabe, steckerungsdict):
    if buchstabe in steckerungsdict.keys():
        buchstabe = steckerungsdict[buchstabe]
        print("gesteckert: ", buchstabe)
    else:
        pass
    return buchstabe


def fortschalten(spruchschluessel, zubuchstabe, walzenlage):
    spruchschluessel[2] = (spruchschluessel[2]+1) % 26 # Fortschalten der ersten Walze bei jedem Tastendruck; Spruchschluessel = Liste der drei Walzenlagen in Zahlen
    print("1. Walze fortgeschaltet")
    print("Position der ersten Walze: ", spruchschluessel[2], "/", zubuchstabe[spruchschluessel[2]])
    if spruchschluessel[2] == walzenlage[2][1]:
        spruchschluessel[1] = (spruchschluessel[1]+1) % 26
        print("2. Walze fortgeschaltet")
        if spruchschluessel[1] == walzenlage[1][1]:
            spruchschluessel[0] = (spruchschluessel[0]+1) % 26
            spruchschluessel[1] = (spruchschluessel[1]+1) % 26 # jetzt sind die Walzen fortgeschaltet
            print("3. und 2. Walze fortgeschaltet")

    return spruchschluessel


def enigma(c, umkehrwalze, walzenlage, ringstellung, steckerverbindungen, spruchschluessel, steckerungsdict, nachricht, zuzahl, zubuchstabe, walzenlage_invers, verwendete_umkehrwalze):
    umkehrwalze = umkehrwalze
    walzenlage = walzenlage
    ringstellung = ringstellung
    steckerverbindungen = steckerverbindungen
    spruchschluessel = spruchschluessel
    steckerungsdict = steckerungsdict
    nachricht = nachricht
    zuzahl = zuzahl
    zubuchstabe = zubuchstabe
    walzenlage = walzenlage
    walzenlage_invers = walzenlage_invers
    global ergebnis
    print("verwendete_umkehrwalze: ", verwendete_umkehrwalze)
    print("----------------------------------------------------------------")
    print("zuzahl = ", zuzahl)
    print("\n", "walzenlage[2][1] = ", walzenlage[2][1])
    for i in nachricht:
        if nachricht.index(i) < 2:
            print()
            print("==========================================================")
            print()
            print("Anfang: ", i)
            if i in ascii_uppercase:
                i = steckern(i, steckerungsdict)
                print("Position der ersten Walze: ", spruchschluessel[2], "/", zubuchstabe[spruchschluessel[2]])
                spruchschluessel = fortschalten(spruchschluessel, zubuchstabe, walzenlage)
                # Momentanzustand: i = Großbuchstabe
                # zuzahl = Buchstabe-Zahl-Dict
                # zubuchstabe = Zahl-Buchstabe-Dict
                i = walzenlage[2][0][zubuchstabe[(zuzahl[i]+spruchschluessel[2])%26]] # Walzen
                print("1. Walze: ", i)
                i = walzenlage[1][0][zubuchstabe[(zuzahl[i]+
                                                  spruchschluessel[1]-spruchschluessel[2])%26]]
                print("2. Walze: ", i)
                i = walzenlage[0][0][zubuchstabe[(zuzahl[i]\
                    +spruchschluessel[0]-spruchschluessel[1]\
                    )%26]]
                print("3. Walze: ", i)  # bis hierhin stimmt´sprint("Umkehrwalze['e']: ", verwendete_umkehrwalze["E"])
                print("umkehrwalze - 3. walze: ", zubuchstabe[(zuzahl[i]-spruchschluessel[0])%26])
                print("wieder ohne Zahl: ", (zuzahl[i]-spruchschluessel[0]))
                i = verwendete_umkehrwalze[zubuchstabe[(zuzahl[i]-spruchschluessel[0])%26]]
                print("Nach Umkehrwalze: ", i)
                print("3. Walze invers['C']: ", walzenlage_invers[0]["C"])
                print("3. Walze invers: ", walzenlage[0][0]["O"])
                print("Zwischenergebnis: ", zuzahl[i])
                i = walzenlage_invers[0][zubuchstabe[(zuzahl[i]+spruchschluessel[0]-spruchschluessel[1]-spruchschluessel[2])%26]]
                print("3. Walze beim zweiten Mal: ", i)
                print("nach der 3. Walze beim zweiten Male (als Zahl): ", zuzahl[i])
                print("zweite Walze: ", walzenlage[1][0])
                i = walzenlage_invers[1][zubuchstabe[(zuzahl[i]+spruchschluessel[0])%26]]
                print("2. Walze beim zweiten Mal: ", i)
                i = walzenlage_invers[2][zubuchstabe[(zuzahl[i]-spruchschluessel[2])%26]]
                print("1. Walze beim zweiten Mal: ", i)
                # sonstiges ...
                if i in steckerungsdict.keys():
                    i = steckerungsdict[i]
                else:
                    pass
                print("zum zweiten Male gesteckert = Ergebnis: ", i)
            ergebnis += i
            print("Ergebnis: ", i)
        else:
            break
    print()
    print("Ergebnis: ", ergebnis)
    c.set("Initialisierung", "Ergebnis", ergebnis)
    file = open(datei, "w")
    c.write(file)

if __name__ == "__main__":
    datei, c, umkehrwalze, walzenlage, ringstellung, steckerverbindungen, spruchschluessel, steckerungsdict, nachricht, zuzahl, zubuchstabe, walzenlage_invers, verwendete_umkehrwalze = initialisierung("test.ini")
    enigma(c, umkehrwalze, walzenlage, ringstellung, steckerverbindungen, spruchschluessel, steckerungsdict, nachricht, zuzahl, zubuchstabe, walzenlage_invers, verwendete_umkehrwalze)
    c.set("Initialisierung", "Ergebnis", ergebnis)
    file = open(datei, "w")
    c.write(file)
Oder habt ihr noch weitere Anmerkungen? Ich habe mich dagegen entschieden, jede einzelne Substitution pro Walze in eine eigene Funktion auszulagern, da das IMHO mit Parameterübergabe etc. zu viel Code-Overhead erzeugen würde.

Danke.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Anonymus: wer dreizehn Rückgabewerte hat, kann schon mal den Überblick verlieren, und noch ein paar Variablen unabsichtlich als global durchschleifen.
In Zeile 3 macht "walzenlage[0] != walzenlage[1] != walzenlage[2]" nicht das was Du denkst (1!=2!=1).
Die fullmatch würde man normalerweise mit einem "if x in (A,B,...)" lösen.
Zeile 61: das ist die komplizierteste Art einen Index für eine Liste zu erzeugen. Mach Dir eine neue Liste!
Zeile 67: ascii_uppercase umzudefinieren ist nicht gerade übersichtlichkeitsfördernd.
Zeile 70: die list kann man sich sparen
Zeile 71: was ist der Vorteil eines Dictionaries mit den Schlüsseln 0 bis 25 gegenüber einer Liste?
Zeile 80ff: Wenn man anfängt Variablen durchzunummerieren, will man eigentlich ein Dictionary.
Zeile 100ff: siehe Zeile 61
Zeile 186ff: wie wärs mit einem Dictionary?
Zeile 228: was soll das del bewirken?
Zeile 238: eine lineare Suche über die Schlüssel eines Dictionaries führt ein Dictionary ad absurdum.
Zeile 246ff: Du solltest Dich entscheiden, ob Du die Liste spruchschluessel ändern willst, oder als Rückgabewert zurückgibst. Nicht beides.
Zeile 262-272: was soll das ????
Zeile 273: jetzt nennst Du das Ding schon ergebnis, ist aber kein Ergebnis der Funktion sondern eine globale Variable! Bitte nicht!
Zeile 278: bei i denkt jeder an eine Zahl, nicht an einen Buchstaben
Zeile 316: siehe Zeile 238
Zeile 319: wenns kein else gibt, kann man es auch ganz weglassen
Zeile 328: die Datei wird nicht wieder geschlossen
Zeile 332-336: das gehört in eine Funktion
Zeile 334-336: nochmal das selbe wie Zeile 327-329?
Die Funktionen sind immer noch zu lang. Es fehlt an Abstraktion.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du solltest erstmal den ganzen doppelten Code loswerden. Zeilen 94 bis 98 sind quasi identisch, 100 bis 112 ebenfalls, 176 bis 181 und 188 bis 120 ebenfalls. Zeilen 80 bis 89 sind einfach nur Strings, Zeilen 160 bis 170 kann man auch zusammenfassen.

Dann ist der ganze Code ein einziges Chaos. Die Funktion ist viel zu lang, Eingabe und Verarbeitung wird überall vermischt und alles ist voll von magischen Zahlen, welche nicht nachvollziehbar sind. Auch verwendest du keinerlei Datenstrukturen, nummerierst Namen durch und drückst alles viel zu kompliziert aus. Warum die Zahlen als Text und warum römische Ziffern?!

Was du dir bei den Zeilen 232 und 333 gedacht hast weiß ich nicht. Wie soll das noch jemand lesen? Und was ist mit 262 bis 272, das ist absolut sinnfrei.

Man könnte jetzt problemlos zwei drei Seiten Texte schreiben was du alles vebessern müsstest. Fange mal mit den von mir genannten Punkten an, dann sind schon mal die schlimmsten Probleme beseitigt. Ich würde dir dringend empfehlen alles neu zu schreiben und in sinnvolle Strukturen zu unterteilen.
Das Leben ist wie ein Tennisball.
BlackJack

@Anonymus: Das Argument mit ”möglichst einfach weil das nach OpenCL übersetzt werden soll” zählt IMHO nicht. Ich habe mir OpenCL jetzt nur oberflächlich angeschaut, aber das scheint auf C (C99) zu basieren, also kann man eigentlich problemlos Datenstrukturen und Funktionen verwenden, und eigentlich auch Klassen, denn solange man keine Vererbung oder Polymorphie verwendet, lässt sich das ganz prima mit ``struct``\s und Funktionen in C ausdrücken. Andererseits verwendest Du in Deinem Code anscheinend ohne Bedenken Wörterbücher (`dict`), ein Datentyp der sich nicht so leicht nach C übernehmen lässt.

Um ”Code-Overhead” solltest Du Dir auch keine Gedanken machen. Erst einmal muss man etwas *richtig* zum Laufen bringen, und das am besten mit Schwerpunkt auf gut lesbaren, und gut organisierten Quelltext. Also eher viele kleine Funktionen die genau eine, in sich geschlossene Aufgabe lösen, und die man einzeln testen kann. Der dabei entstehende ”Overhead” ist etwas mit dem aktuelle Rechner prima klar kommen. *So* rechenintensiv ist das nicht was Du da machst, da kann man ruhig viele Aufrufe dazu packen, wenn es die Sache leichter nachvollziehbar macht. Wenn man dass dann nach C oder OpenCL portiert kann man immer noch von Hand ”inlining” betreiben, oder Makros statt Funktionen verwenden wo das Sinn macht.

Bei den nummerierten Namen, egal ob das nun tatsächlich Nummern, römische Ziffern, oder fortlaufende Buchstabenzusätze sind, bietet sich ein Wörterbuch an. Damit spart man sich dann auch die ``if``/``elif``-Kaskaden um einen Namen auszuwählen, weil man ganz einfach die Auswahlvariable als Schlüssel verwenden kann. Die Grunddaten für die ganzen Walzen könnte man zum Beispiel so kodieren:

Code: Alles auswählen

    #: Maps wheel names to tuples of the form (character order,
    #: notch positions, fixed wheel y/n)
    NAME_TO_DATA = {
        # 
        # Entry wheel.
        # 
        'entry_wheel': (ascii_lowercase, '', True),
        # 
        # Rotating wheels.
        # 
        'I': ('ekmflgdqvzntowyhxuspaibrcj', 'r', False),
        'II': ('ajdksiruxblhwtmcqgznpyfvoe', 'f', False),
        'III': ('bdfhjlcprtxvznyeiwgakmusqo', 'w', False),
        'IV': ('esovpzjayquirhxlnftgkdcmwb', 'k', False),
        'V': ('vzbrgityupsdnhlxawmjqofeck', 'a', False),
        'VI': ('jpgvoumfyqbenhzrdkasxlictw', 'an', False),
        'VII': ('nzjhgrcxmyswboufaivlpekqdt', 'an', False),
        'VIII': ('fkqhtlxocbjspdzramewniuygv', 'an', False),
        # 
        # Reflectors.
        # 
        'A': ('ejmzalyxvbwfcrquontspikhgd', '', True),
        'B': ('yruhqsldpxngokmiebfzcwvjat', '', True),
        'C': ('fvpjiaoyedrzxwgctkuqsbnmhl', '', True),
    }
Die Daten von einem Eintrag kann man dann hernehmen um eine Datenstruktur zu erzeugen, die einen Rotor und seinen Zustand repräsentiert. Die Ringstellung, die aktuelle Position, und die Buchstabenreihenfolge so aufbereitet, dass man leicht in beide Richtungen konvertieren kann. Ich persönlich würde das auf der Ebene alles mit Zahlen zwischen 0 und 25 machen, die als Indizes in ”Übersetzungslisten” verwendet werden können, weil das sehr leicht in C umzusetzen ist, und damit auch OpenCL näher kommt. Aber selbst bei einer reinen Python-Lösung würde das Sinn machen, weil man bei Buchstaben und Wörterbüchern sonst immer zwischen Buchstaben und Zahlen hin und her rechnen müsste wenn man eine Abbildung macht. Denn man muss auf den Index ja auch immer mindestens die Position addieren und je nachdem wie man das mit der Ringeinstellung löst, die auch noch berücksichtigen.
Anonymus
User
Beiträge: 3
Registriert: Samstag 21. Juni 2014, 11:09

Danke an alle für die zahlreichen Hinweise. Ich werde mich so schnell es geht darum kümmern, da ich allerdings diese Woche verreist sein werde, bitte ich um etwas Geduld.

Nochmals Danke.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Anonymus:
Eigentlich ist die Umsetzung der Enigma für die GPU nicht so gut geeignet, da die Entschlüsselung nachfolgender Zeichen von vorhergehenden stufenweise abhängig ist. Das Problem ist nur teilweise auf Rotorebene parallelisierbar, wenn man die Umkehrscheibe weglässt. Was natürlich geht - tausende Enigmas gleichzeitig ;)
BlackJack

@jerch: 1000 gleichzeitig würde sich eignen um „brute force” Einstellungen für eine gegebene Nachricht durchzuprobieren. :-)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@BlackJack: Die Turingbombe "selbst" auf der GPU zu entwickeln (also ohne Vorwissen von Herrn Turing zu nutzen, nur mit Kenntnis der Funktionalität der Enigma) - klingt nach einer Herausforderung :D
Antworten