Added crib-based pocket engima breaking
authorNeil Smith <neil.git@njae.me.uk>
Sat, 5 Jul 2014 20:57:36 +0000 (21:57 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Sat, 5 Jul 2014 20:57:36 +0000 (21:57 +0100)
cipher.py
cipherbreak.py

index 18f493b9dfadb22295dabf7a288ee8fd9cf51b31..0fb6dc255d4ce42656783b1e28f7c2caa379a0b0 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -584,18 +584,18 @@ class PocketEnigma(object):
                     [p[1] for p in wheel_spec])) != 26:
             raise ValueError("Wheel specification does not contain 26 letters")
 
-    def encipher(self, letter):
+    def encipher_letter(self, letter):
         """Enciphers a single letter, by advancing the wheel before looking up
         the letter on the wheel.
 
         >>> pe.set_position('f')
         5
-        >>> pe.encipher('k')
+        >>> pe.encipher_letter('k')
         'h'
         """
         self.advance()
         return self.lookup(letter)
-    decipher = encipher
+    decipher_letter = encipher_letter
 
     def lookup(self, letter):
         """Look up what a letter enciphers to, without turning the wheel.
@@ -618,23 +618,23 @@ class PocketEnigma(object):
         self.position = (self.position + 1) % 26
         return self.position
 
-    def encipher_message(self, message):
+    def encipher(self, message):
         """Enciphers a whole message.
 
         >>> pe.set_position('f')
         5
-        >>> pe.encipher_message('helloworld')
+        >>> pe.encipher('helloworld')
         'kjsglcjoqc'
         >>> pe.set_position('f')
         5
-        >>> pe.encipher_message('kjsglcjoqc')
+        >>> pe.encipher('kjsglcjoqc')
         'helloworld'
         """
         transformed = ''
         for l in message:
-            transformed += self.encipher(l)
+            transformed += self.encipher_letter(l)
         return transformed
-    decipher_message = encipher_message
+    decipher = encipher
 
     def set_position(self, position):
         """Sets the position of the wheel, by specifying the letter the arrow
index c2c5360548b2ffa8c7c5ea8f1ec6baa30c18f3d3..6e08f49b05f327e24481734b22d115f727048dd9 100644 (file)
@@ -401,6 +401,34 @@ def beaufort_frequency_break(message, max_key_length=20, fitness=Pletters):
     return max(results, key=lambda k: k[1])
 
 
+def pocket_enigma_break_by_crib(message, wheel_spec, crib, crib_position):
+    """Break a pocket enigma using a crib (some plaintext that's expected to
+    be in a certain position). Returns a list of possible starting wheel
+    positions that could produce the crib.
+
+    >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'h', 0)
+    ['a', 'f', 'q']
+    >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'he', 0)
+    ['a']
+    >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'll', 2)
+    ['a']
+    >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 2)
+    ['a']
+    >>> pocket_enigma_break_by_crib('kzpjlzmoga', 1, 'l', 3)
+    ['a', 'j', 'n']
+    >>> pocket_enigma_break_by_crib('aaaaa', 1, 'l', 3)
+    []
+    """
+    pe = PocketEnigma(wheel=wheel_spec)
+    possible_positions = []
+    for p in string.ascii_lowercase:
+        pe.set_position(p)
+        plaintext = pe.decipher(message)
+        if plaintext[crib_position:crib_position+len(crib)] == crib:
+            possible_positions += [p]
+    return possible_positions
+
+
 def plot_frequency_histogram(freqs, sort_key=None):
     x = range(len(freqs.keys()))
     y = [freqs[l] for l in sorted(freqs.keys(), key=sort_key)]