/usr/lib/python3/dist-packages/enigma/plugboard.py is in python3-enigma 0.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | # Copyright (C) 2012 by Brian Neal.
# This file is part of Py-Enigma, the Enigma Machine simulation.
# Py-Enigma is released under the MIT License (see License.txt).
"""Contains the Plugboard class for simulating the plugboard (Steckerbrett)
component of the Enigma Machine.
"""
import collections
from itertools import chain
import string
# On Heer & Luftwaffe (?) models, the plugs are labeled with upper case letters
HEER_LABELS = string.ascii_uppercase
# The number of plugboard cables supplied with a machine:
MAX_PAIRS = 10
class PlugboardError(Exception):
pass
class Plugboard:
"""The plugboard allows the operator to swap letters before and after the
entry wheel. This is accomplished by connecting cables between pairs of
plugs that are marked with letters (Heer & Luftwaffe models) or numbers
(Kriegsmarine). Ten cables were issued with each machine; thus up to 10 of
these swappings could be used as part of a machine setup.
Each cable swaps both the input and output signals. Thus if A is connected
to B, A crosses to B in the keyboard to entry wheel direction and also in
the reverse entry wheel to lamp direction.
"""
def __init__(self, wiring_pairs=None):
"""Configure the plugboard according to a list or tuple of integer
pairs, or None.
A value of None or an empty list/tuple indicates no plugboard
connections are to be used (i.e. a straight mapping).
Otherwise wiring_pairs must be an iterable of integer pairs, where each
integer is between 0-25, inclusive. At most 10 such pairs can be
specified. Each value represents an input/output path through the
plugboard. It is invalid to specify the same path more than once in the
list.
If an invalid wiring_pairs parameter is given, a PlugboardError is
raised.
"""
# construct wiring mapping table with default 1-1 mappings
self.wiring_map = list(range(26))
# use settings if provided
if not wiring_pairs:
return
if len(wiring_pairs) > MAX_PAIRS:
raise PlugboardError('Please specify %d or less pairs' % MAX_PAIRS)
# ensure a path occurs at most once in the list
counter = collections.Counter(chain.from_iterable(wiring_pairs))
path, count = counter.most_common(1)[0]
if count != 1:
raise PlugboardError('duplicate connection: %d' % path)
# make the connections
for pair in wiring_pairs:
m = pair[0]
n = pair[1]
if not (0 <= m < 26) or not (0 <= n < 26):
raise PlugboardError('invalid connection: %s' % str(pair))
self.wiring_map[m] = n
self.wiring_map[n] = m
@classmethod
def from_key_sheet(cls, settings=None):
"""Configure the plugboard according to a settings string as you may
find on a key sheet.
Two syntaxes are supported, the Heer/Luftwaffe and Kriegsmarine styles:
In the Heer syntax, the settings are given as a string of
alphabetic pairs. For example: 'PO ML IU KJ NH YT GB VF RE DC'
In the Kriegsmarine syntax, the settings are given as a string of number
pairs, separated by a '/'. Note that the numbering uses 1-26, inclusive.
For example: '18/26 17/4 21/6 3/16 19/14 22/7 8/1 12/25 5/9 10/15'
To specify no plugboard connections, settings can be None or an empty
string.
A PlugboardError will be raised if the settings string is invalid, or if
it contains more than MAX_PAIRS pairs. Each plug should be present at
most once in the settings string.
"""
if not settings:
return cls(None)
wiring_pairs = []
# detect which syntax is being used
if settings.find('/') != -1:
# Kriegsmarine syntax
pairs = settings.split()
for p in pairs:
try:
m, n = p.split('/')
m, n = int(m), int(n)
except ValueError:
raise PlugboardError('invalid pair: %s' % p)
wiring_pairs.append((m - 1, n - 1))
else:
# Heer/Luftwaffe syntax
pairs = settings.upper().split()
for p in pairs:
if len(p) != 2:
raise PlugboardError('invalid pair: %s' % p)
m = p[0]
n = p[1]
if m not in HEER_LABELS or n not in HEER_LABELS:
raise PlugboardError('invalid pair: %s' % p)
wiring_pairs.append((ord(m) - ord('A'), ord(n) - ord('A')))
return cls(wiring_pairs)
def signal(self, n):
"""Simulate a signal entering the plugboard on wire n, where n must be
an integer between 0 and 25.
Returns the wire number of the output signal (0-25).
Note that since the plugboard always crosses pairs of wires, it doesn't
matter what direction (keyboard -> entry wheel or vice versa) the signal
is coming from.
"""
return self.wiring_map[n]
|