X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=cipher.py;fp=cipher.py;h=cd75d2cc505f61d788ac190cc186810ff32f6b04;hb=1ca546941ac408759d3b10bc12d00a5d6b1b130a;hp=6cce72543224f02468e8a7609b674d158d238127;hpb=674b12c1ed3d8c4a6007f1744afb7a5fbf90fb6c;p=cipher-tools.git diff --git a/cipher.py b/cipher.py index 6cce725..cd75d2c 100644 --- a/cipher.py +++ b/cipher.py @@ -3,6 +3,9 @@ import collections import math from enum import Enum from itertools import zip_longest, cycle, chain +import numpy as np +from numpy import matrix +from numpy import linalg from language_models import * @@ -630,6 +633,44 @@ def railfence_decipher(message, height, fillvalue=''): return ''.join(c for r in zip_longest(*(down_rows + up_rows), fillvalue='') for c in r) +def hill_encipher(matrix, message_letters, fillvalue='a'): + """Hill cipher + + >>> hill_encipher(np.matrix([[7,8], [11,11]]), 'hellothere') + 'drjiqzdrvx' + >>> hill_encipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \ + 'hello there') + 'tfjflpznvyac' + """ + n = len(matrix) + sanitised_message = sanitise(message_letters) + if len(sanitised_message) % n != 0: + padding = fillvalue[0] * (n - len(sanitised_message) % n) + else: + padding = '' + message = [ord(c) - ord('a') for c in sanitised_message + padding] + message_chunks = [message[i:i+n] for i in range(0, len(message), n)] + # message_chunks = chunks(message, len(matrix), fillvalue=None) + enciphered_chunks = [((matrix * np.matrix(c).T).T).tolist()[0] + for c in message_chunks] + return ''.join([chr(int(round(l)) % 26 + ord('a')) + for l in sum(enciphered_chunks, [])]) + + +def hill_decipher(matrix, message, fillvalue='a'): + """Hill cipher + + >>> hill_decipher(np.matrix([[7,8], [11,11]]), 'drjiqzdrvx') + 'hellothere' + >>> hill_decipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \ + 'tfjflpznvyac') + 'hellothereaa' + """ + adjoint = linalg.det(matrix)*linalg.inv(matrix) + inverse_determinant = modular_division_table[int(round(linalg.det(matrix))) % 26][1] + inverse_matrix = (inverse_determinant * adjoint) % 26 + return hill_encipher(inverse_matrix, message, fillvalue) + class PocketEnigma(object): """A pocket enigma machine The wheel is internally represented as a 26-element list self.wheel_map,