| 48 | |
| 49 | |
| 50 | class HillCipher: |
| 51 | key_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
| 52 | # This cipher takes alphanumerics into account |
| 53 | # i.e. a total of 36 characters |
| 54 | |
| 55 | replaceLetters = lambda self, letter: self.key_string.index(letter) |
| 56 | replaceNumbers = lambda self, num: self.key_string[round(num)] |
| 57 | |
| 58 | # take x and return x % len(key_string) |
| 59 | modulus = numpy.vectorize(lambda x: x % 36) |
| 60 | |
| 61 | toInt = numpy.vectorize(lambda x: round(x)) |
| 62 | |
| 63 | def __init__(self, encrypt_key): |
| 64 | """ |
| 65 | encrypt_key is an NxN numpy matrix |
| 66 | """ |
| 67 | self.encrypt_key = self.modulus(encrypt_key) # mod36 calc's on the encrypt key |
| 68 | self.checkDeterminant() # validate the determinant of the encryption key |
| 69 | self.decrypt_key = None |
| 70 | self.break_key = encrypt_key.shape[0] |
| 71 | |
| 72 | def checkDeterminant(self): |
| 73 | det = round(numpy.linalg.det(self.encrypt_key)) |
| 74 | |
| 75 | if det < 0: |
| 76 | det = det % len(self.key_string) |
| 77 | |
| 78 | req_l = len(self.key_string) |
| 79 | if gcd(det, len(self.key_string)) != 1: |
| 80 | raise ValueError("discriminant modular {0} of encryption key({1}) is not co prime w.r.t {2}.\nTry another key.".format(req_l, det, req_l)) |
| 81 | |
| 82 | def processText(self, text): |
| 83 | text = list(text.upper()) |
| 84 | text = [char for char in text if char in self.key_string] |
| 85 | |
| 86 | last = text[-1] |
| 87 | while len(text) % self.break_key != 0: |
| 88 | text.append(last) |
| 89 | |
| 90 | return ''.join(text) |
| 91 | |
| 92 | def encrypt(self, text): |
| 93 | text = self.processText(text.upper()) |
| 94 | encrypted = '' |
| 95 | |
| 96 | for i in range(0, len(text) - self.break_key + 1, self.break_key): |
| 97 | batch = text[i:i+self.break_key] |
| 98 | batch_vec = list(map(self.replaceLetters, batch)) |
| 99 | batch_vec = numpy.matrix([batch_vec]).T |
| 100 | batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0] |
| 101 | encrypted_batch = ''.join(list(map(self.replaceNumbers, batch_encrypted))) |
| 102 | encrypted += encrypted_batch |
| 103 | |
| 104 | return encrypted |
| 105 | |
| 106 | def makeDecryptKey(self): |
| 107 | det = round(numpy.linalg.det(self.encrypt_key)) |