-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaffine_classes.py
107 lines (85 loc) · 3.25 KB
/
affine_classes.py
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
# -*- coding: utf-8 -*-
from math import gcd
from itertools import chain, groupby
from affine_alphabet import *
class Message:
# global alphSpecials
def _flatten(self,listOfLists):
"""Flatten one level of nesting"""
return chain.from_iterable(listOfLists)
def _rBlanks(self,strng):
"""Removes blanks of a string strng and converts to uppercase"""
return ''.join(strng.split()).upper()
def _normalize(self,strng):
"""
Removes blanks spaces of the string 'strng'; then removes accents
according to 'alphSpecials'. If character 'ñ' occurs in 'strng' then
'GN' appears in 'accum' as an entry, therefore '_flatten' is needed.
"""
s = self._rBlanks(strng)
accum = []
for ch in s:
if ch in alphSpecials:
accum.append(alphSpecials[ch])
else:
accum.append(ch)
return filter(lambda x: x in alphabet,self._flatten(accum))
#return [c for c in self._flatten(accum) if c in alphabet]
def __init__(self,strng):
x = self._normalize(strng)
self.content = ''.join(x)
self.length = len(self.content)
def __str__(self):
return self.content
class Encipher(Message):
# global n, m, f, chNum, numCh
def _invMod(self,a:int,n:int) -> int:
"""Return multiplicative inverse of a modulo n.
If the integers a and n are not coprime, then return 0."""
try:
x, g = pow(a,-1,n), 1
except ValueError:
g = 0
return int(g==1 and x)
def _translation(self,c,a,b):
return numCh[format((a*int(chNum[c])+b)%len(alphabet),f)]
def __init__(self,strng,a=1,b=0):
Message.__init__(self,strng)
self.decimation = a
self.displacement = b
def affine(self,mode=True):
"""Memoization based code mode: True for deciphering"""
mem, accum = {}, []
if mode:
a = self._invMod(self.decimation,len(alphabet))
b = -a*self.displacement
else:
a, b = self.decimation, self.displacement
for ch in self.content:
if ch in mem:
accum.append(mem[ch])
else:
mem[ch]=self._translation(ch,a,b)
accum.append(mem[ch])
return ''.join(accum)
class ChiSquareAttack(Encipher):
# global alphFreq
def __init__(self,strng):
Encipher.__init__(self,strng)
def rfrec(self, strng):
return {k:len(list(g))/len(strng) for k, g in groupby(''.join(sorted(strng)))}
def chiSquared(self, strng):
inventory = dict.fromkeys(alphabet,0)
inventory.update(self.rfrec(strng))
chDegree =[(len(strng)*(inventory[ch]-alphFreq[ch]))**2/alphFreq[ch] for ch in inventory]
return sum(chDegree)
def chiSquaredTest(self) -> list:
# n = len(alphabet)
candidates = []
for a in range(1, n):
if gcd(a,n) == 1:
for b in range(n):
T = Encipher(self.content,a,b)
dec = T.affine(True)
candidates.append((a, b, self.chiSquared(dec)))
return sorted(candidates, key = lambda x: x[2])