© Teguh Jati Prasetyo/Shutterstock.com
Implementierung einer Enigma

Der Weg zum Geheimalphabet


Im letzten Teil unserer vierteiligen Serie geht es jetzt an die Implementierung der Enigma mit React und JavaScript von der Tastatur über die Eintrittswalze bis zum Lampenfeld.

Nach den vielen Hintergrundinformationen, die in den vorhergehenden Teilen gegeben wurden, kommen wir nun zur Implementierung. Wir werden eine Enigma I mit folgenden Komponenten implementieren:

  • Tastatur

  • Steckerbrett

  • Eintrittswalze

  • drei bewegliche Walzen (Rotoren)

  • eine unbewegliche Umkehrwalze

  • Lampenfeld

Zu Beginn schauen wir uns das Ende des Quellcodes an. Der Quellcode wird nicht komplett abgedruckt, da er ca. 550 Zeilen lang ist, er ist aber online auf www.entwickler.de verfügbar [1]. Dort befindet sich ebenfalls die Datei enigma.css, die das Stylesheet für die Darstellung enthält, welches wir hier nicht abdrucken.

Der Code in Listing 1 konfiguriert unsere Enigma: rotor2Schema, das der Walze 3 entspricht, wird das Verdrahtungsschema des dazugehörigen Walzentyps zugeordnet. rotor2Position ist mit der Walzenposition belegt und rotor2Offset enthält die Verstelldifferenz des sichtbaren Buchstabens zum intern verwendeten Buchstaben. Analog werden die anderen Walzen konfiguriert. Das entspricht einer Rotoreneinstellung „BAS“.

Listing 1

ReactDOM.render( <Enigma rotor2Schema={ROTOR_I_SCHEMA} rotor1Schema={ROTOR_IV_SCHEMA} rotor0Schema={ROTOR_III_SCHEMA} rotor2Position={1} rotor1Position={0} rotor0Position={18} rotor2Offset={0} rotor1Offset={0} rotor0Offset={0} />, document.getElementById('root') );

Die Walzenschemata sind an anderer Stelle als Konstanten definiert:

const ROTOR_I_SCHEMA = ["EKMFLGDQVZNTOWYHXUSPAIBRCJ", "R"]; const ROTOR_III_SCHEMA = ["BDFHJLCPRTXVZNYEIWGAKMUSQO", "W"]; const ROTOR_IV_SCHEMA = ["ESOVPZJAYQUIRHXLNFTGKDCMWB", "K"];

Das erste Element der Liste enthält dabei das Geheimalphabet, das zweite die Position der Übertragskerbe. Bei deren Erreichen wird der weiter links liegende Rotor um eine Position mitrotiert.

Listing 2 enthält den Konstruktor der Klasse Enigma. Hier werden im Wesentlichen die übergebenen Properties ausgelesen. Daraus werden die sichtbaren Buchstaben für die Anzeige berechnet (visibleRotorPosition2, visibleRotorPosition1, visibleRotorPosition0). Außerdem werden die Konfigurationsdaten für die Rotoren in den Arrays this.rotorConfigs, this.rotorPositions und this.rotorOffsets gespeichert, um später bei der Anzeige über diese Arrays in einer Schleife iterieren zu können.

Listing 2

class Enigma extends React.Component { constructor(props) { super(props); let { rotor2Schema, rotor1Schema, rotor0Schema, rotor2Position, rotor1Position, rotor0Position, rotor2Offset, rotor1Offset, rotor0Offset } = props; this.state = { pressedKey: "", plugBoardDecoded: "", visibleRotorPosition2: visiblePosition(rotor2Position, rotor2Offset), visibleRotorPosition1: visiblePosition(rotor1Position, rotor1Offset), visibleRotorPosition0: visiblePosition(rotor0Position, rotor0Offset), }; [ this.rotor2Position, this.rotor1Position, this.rotor0Position, this.rotor2Offset, this.rotor1Offset, this.rotor0Offset ] = [ rotor2Position, rotor1Position, rotor0Position, rotor2Offset, rotor1Offset, rotor0Offset ]; this.rotorConfigs = []; this.rotorPositions = []; this.rotorOffsets = []; this.rotorLeftArrow = []; this.rotorRightArrow = []; let rotorParams = [ [rotor0Schema, rotor0Position, rotor0Offset], [rotor1Schema, rotor1Position, rotor1Offset], [rotor2Schema, rotor2Position, rotor2Offset], ]; for (let i = 0; i < rotorParams.length; i++) { this.rotorConfigs[i] = rotorParams[i][0]; this.rotorPositions[i] = rotorParams[i][1]; this.rotorOffsets[i] = rotorParams[i][2]; } } ...

Listing 3 zeigt die render()-Methoden der Klasse Enigma.

Listing 3

 renderRotors() { let rotors = [] for (let pos = this.rotorConfigs.length - 1; pos >= 0; pos--) { let rotorId = "rotor" + pos; let rotorLeftArrow = rotorId + "Encoded"; let rotorRightArrow = rotorId + "Decoded"; let rotor = <Rotor ref={rotorId} key={pos} slot={pos} rotorSchema={this.rotorConfigs[pos]} rotorPosition={this.rotorPositions[pos]} rotorOffset={this.rotorOffsets[pos]} leftArrow={this.state[rotorLeftArrow]} rightArrow={this.state[rotorRightArrow]} />; rotors.push( rotor ); } return rotors; } renderRotorDisplay(visiblePosition2, visiblePosition1, visiblePosition0) { let visiblePositions = [visiblePosition2, visiblePosition1, visiblePosition0]; let displayedPositions = []; for (let i = 0; i < visiblePositions.length; i++) { displayedPositions.push( this.renderChar(visiblePositions[i], 260 - (i * 65), 80) ) } return ( <div className="RotorDisplay"> {displayedPositions} </div> ); } render() { return ( <div> <div className="EnigmaAnchor"> <div className="EnigmaInternals"> <Reflector ref="reflector" swapped={UKW_B_SCHEMA} rightArrow={this.state.reflectorEncoded}/> {this.renderRotors()} <EntryWheel leftArrow={this.state.plugBoardEncoded} rightArrow={this.state.entryWheel2PlugBoard}/> <PlugBoard ref="plugBoard" swapped={PLUGBOARD_CONFIG} leftArrow={this.state.plugBoardEncoded} rightArrow={this.state.plugBoardDecoded} /> <KeyOrLamp pressedKey={this.state.pressedKey} activeLamp={this.state.plugBoardDecoded} leftArrow={this.state.pressedKey}/> </div> </div> <div className="RotorDisplayAnchor"> {this.renderRotorDisplay( this.state.visibleRotorPosition0, this.state.visibleRotorPosition1, this.state.visibleRotorPosition2) } <Lampboard ref="lampBoard" activeLamp={this.state.plugBoardDecoded}/> <Keyboard buttonDownCallBack={this.encode.bind(this)} buttonUpCallBack={this.next.bind(this)} pressedKey={this.state.pressedKey} /> </div> </div> ); } ...

renderRotors() benutzt die im Konstruktor belegten Arrays, um die Rotoren zu zeichnen. Das geschieht, indem in einer Schleife über die Arrays iteriert wird und entsprechende Rotor-Elemente angelegt werden.

renderChar(theChar, x, y) (nicht abgedruckt, siehe [1]) zeichnet einfach den Buchstaben theChar an Position (x, y) in einer abgerundeten Box. Die Methode wird für die Darstellung der Rotoranzeige über dem Lampenfeld verwendet. Das geschieht in der Methode renderRotorDisplay(visiblePosition2, visiblePosition1, visiblePosition0), der die berechneten sichtbaren Positionen übergeben werden und die diese in einer Schleife über den Aufruf von renderChar() anzeigt.

In der eigentlichen render()-Methode werden in dem Codeabschnitt (Listing 4) Elemente für

  • die Umkehrwalze (Reflector, hier vom Typ B),

  • die drei beweglichen Walzen (this.renderRotors()),

  • die unbewegliche Eintrittswalze (EntryWheel),

  • das Steckerbrett (Plugboard), sowie eine virtuelle Komponente und

  • KeyOrLamp, das nur zur Darstellung des Eingabe- und Ausgabebuchstabens dient,

angelegt.

Listing 4

 <Reflector ref="reflector" swapped={UKW_B_SCHEMA} rightArrow={this.state.reflectorEncoded}/> {this.renderRotors()} <EntryWheel leftArrow={this.state.plugBoardEncoded} rightArrow={this.state.entryWheel2PlugBoard}/> <PlugBoard ref="plugBoard" swapped={PLUGBOARD_CONFIG} leftArrow={this.state.plugBoardEncoded} rightArrow={this.state.plugBoardDecoded} /> <KeyOrLamp pressedKey={this.state.pressedKey} activeLamp={this.state.plugBoardDecoded} leftArrow={this.state.pressedKey}/>

Die Properties leftArrow und rightArrow werden belegt, wenn der Strom eine Komponente links beziehungsweise rechts verlässt. Reflector und PlugBoard wird ein Array mit Vertauschungen übergeben. Sie sind wie folgt definiert:

const PLUGBOARD_CONFIG = [ "AD", "CN", "ET", "FL", "GI", "JV", "KZ", "PU", "QY", "WX"]; const UKW_B_SCHEMA = [ "AY", "BR", "CU", "DH", "EQ", "FS", "GL", "IP", "JX", "KN", "MO", "TZ", "VW"];

Die Komponente KeyOrLamp bekommt zusätzlich den gerade gedrückten Buchstaben und die aktivierte Lampe im Lampenfeld übergeben. Weiter im Code wird dann mit this.renderRotorDisplay() die Darstellung der Walzenstellung vorgenommen. Außerdem wird mit LampBoard das Lampenfeld und mit Keyboard die Tastatur erzeugt (Listing 5):

Listing 5

 {this.renderRotorDisplay( this.state.visibleRotorPosition0, this.state.visibleRotorPosition1, this.state.visibleRot...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang