75048f9a52ddd3840ef19d614455de848f314153
1 from utilities
import *
2 from language_models
import *
5 from numpy
import matrix
6 from numpy
import linalg
8 from logger
import logger
11 def hill_encipher(matrix
, message_letters
, fillvalue
='a'):
14 >>> hill_encipher(np.matrix([[7,8], [11,11]]), 'hellothere')
16 >>> hill_encipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \
21 sanitised_message
= sanitise(message_letters
)
22 if len(sanitised_message
) % n
!= 0:
23 padding
= fillvalue
[0] * (n
- len(sanitised_message
) % n
)
26 message
= [pos(c
) for c
in sanitised_message
+ padding
]
27 message_chunks
= [message
[i
:i
+n
] for i
in range(0, len(message
), n
)]
28 # message_chunks = chunks(message, len(matrix), fillvalue=None)
29 enciphered_chunks
= [((matrix
* np
.matrix(c
).T
).T
).tolist()[0]
30 for c
in message_chunks
]
31 return cat([unpos(round(l
))
32 for l
in sum(enciphered_chunks
, [])])
34 def hill_decipher(matrix
, message
, fillvalue
='a'):
37 >>> hill_decipher(np.matrix([[7,8], [11,11]]), 'drjiqzdrvx')
39 >>> hill_decipher(np.matrix([[6, 24, 1], [13, 16, 10], [20, 17, 15]]), \
43 adjoint
= linalg
.det(matrix
)*linalg
.inv(matrix
)
44 inverse_determinant
= modular_division_table
[int(round(linalg
.det(matrix
))) % 26][1]
45 inverse_matrix
= (inverse_determinant
* adjoint
) % 26
46 return hill_encipher(inverse_matrix
, message
, fillvalue
)
48 def hill_break(message
, matrix_size
=2, fitness
=Pletters
,
49 number_of_solutions
=1, chunksize
=500):
51 all_matrices
= [np
.matrix(list(m
))
52 for m
in itertools
.product([list(r
)
53 for r
in itertools
.product(range(26), repeat
=matrix_size
)],
55 valid_matrices
= [m
for m
, d
in
56 zip(all_matrices
, (int(round(linalg
.det(m
))) for m
in all_matrices
))
60 with multiprocessing
.Pool() as pool
:
61 helper_args
= [(message
, matrix
, fitness
)
62 for matrix
in valid_matrices
]
63 # Gotcha: the helper function here needs to be defined at the top level
64 # (limitation of Pool.starmap)
65 breaks
= pool
.starmap(hill_break_worker
, helper_args
, chunksize
)
66 if number_of_solutions
== 1:
67 return max(breaks
, key
=lambda k
: k
[1])
69 return sorted(breaks
, key
=lambda k
: k
[1], reverse
=True)[:number_of_solutions
]
71 def hill_break_worker(message
, matrix
, fitness
):
72 plaintext
= hill_decipher(matrix
, message
)
73 fit
= fitness(plaintext
)
74 logger
.debug('Hill cipher break attempt using key {0} gives fit of '
75 '{1} and decrypt starting: {2}'.format(matrix
,
76 fit
, sanitise(plaintext
)[:50]))
79 if __name__
== "__main__":