tdd-deciphered.com

created by @parsingphase

Chapter 14: Missing components: plugboard

Two further elements are required to assemble a complete Enigma simulation. How do these fit into our system?

The best way to answer this is to look at the full electrical path from input key to output lamp.

The first element encountered is the plugboard (Steckerbrett) that we’ve not yet described or yet implemented. This consists of a board on the front of the Enigma with 26 pairs of sockets marked A-Z. When nothing is plugged into these sockets, they simply map A-A, B-B etc and so have no enciphering effect. However, when a twin doubled-headed cable is plugged between two pairs, the letters are swapped over. Typically, around 10 cables were used at once.

The second element reached is our first rotor slot, from where the signal routes through the rotor core, out to the second slot, through that core, to the third slot, through that core, and to our second missing element. This is the Reflector, which can be thought of as a one-sided Rotor with a fixed core, in as much as it takes a signal in at one point on its right-hand side and outputs it at a different location on the same side.

The signal is now routed back through the rotors and slots (this time, travelling left to right) and then out though the plugboard, to a display lamp.

The elements we need to add, then, are the plugboard, the reflector, and the ability to pass signals right-to-left though our rotors and slots.

We can start off by creating some tests for the Plugboard, in tests/Phase/Enigma/PlugboardTest.php

<?php
namespace Phase\Enigma;

class PlugboardTest extends \PHPUnit_Framework_TestCase
{
    /**
     * Test various cable combinations, swapped and non-swapped characters
     * @dataProvider connectionsDataProvider
     * @param $connections Array pairs of letters
     * @param $in String single character
     * @param $out String single character
     * @return null
     */
    function testSubstitutions($connections, $in, $out)
    {
        $plugboard = new Plugboard();
        $plugboard->setCableConnections($connections);

        $this->assertSame(
            $out,
            $plugboard->getOutputCharacterForInputCharacter($in),
            "Plugboard must substitute pair as specified"
        );
        $this->assertSame(
            $in,
            $plugboard->getOutputCharacterForInputCharacter($out),
            "Plugboard substitution must be reciprocal"
        );
    }

    function connectionsDataProvider()
    {
        $testData = [
            [[], 'A', 'A'], // Test identity mapping with no cables plugged
            [   // set cables and an input that will swap
                [
                    'D' => 'N',
                    'G' => 'R',
                    'I' => 'S',
                    'K' => 'C',
                    'Q' => 'X',
                    'T' => 'M',
                    'P' => 'V',
                    'H' => 'Y',
                    'F' => 'W',
                    'B' => 'J'
                ],
                'K',
                'C',
            ],
            [ // set cables and an input that won't swap
                [
                    'D' => 'N',
                    'G' => 'R',
                    'I' => 'S',
                    'K' => 'C',
                    'Q' => 'X',
                    'T' => 'M',
                    'P' => 'V',
                    'H' => 'Y',
                    'F' => 'W',
                    'B' => 'J'
                ],
                'E',
                'E',
            ],
            [
                [
                    'A' => 'T',
                    'B' => 'L',
                    'D' => 'F',
                    'G' => 'J',
                    'H' => 'M',
                    'N' => 'W',
                ],
                'H',
                'M',
            ],
            [
                [
                    'A' => 'T',
                    'B' => 'L',
                    'D' => 'F',
                    'G' => 'J',
                    'H' => 'M',
                    'N' => 'W',
                ],
                'C',
                'C',
            ]
        ];

        return ($testData);
    }
}

and implement:

<?php
namespace Phase\Enigma;

class Plugboard implements EncryptorInterface
{
    protected $connections = [];

    public function clearAllConnections()
    {
        $this->connections = [];
    }

    public function addCableConnection($from, $to)
    {
        if (!(preg_match('/^[A-Z]$/', $from) && preg_match('/^[A-Z]$/', $to))) {
            throw new \InvalidArgumentException("Invalid pair $from-$to");
        }

        if (isset($this->connections[$from])) {
            throw new \InvalidArgumentException("Socket $from already used");
        }

        if (isset($this->connections[$to])) {
            throw new \InvalidArgumentException("Socket $to already used");
        }

        $this->connections[$from] = $to;
        $this->connections[$to] = $from;
    }

    public function setCableConnections($assocArray)
    {
        $this->clearAllConnections();

        foreach ($assocArray as $from => $to) {
            $this->addCableConnection($from, $to);
        }
    }

    public function getOutputCharacterForInputCharacter($inputCharacter)
    {
        if (isset($this->connections[$inputCharacter])) {
            $char = $this->connections[$inputCharacter];
        } else {
            $char = $inputCharacter;
        }

        return $char;
    }
}

Tag: Chapter14-1-Plugboard