forked from bozhu/IDEA-Python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
idea.py
135 lines (98 loc) · 3.1 KB
/
idea.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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python
# A Python implementation of the block cipher IDEA
# Copyright (c) 2015 Bo Zhu https://about.bozhu.me
# MIT License
def _mul(x, y):
assert 0 <= x <= 0xFFFF
assert 0 <= y <= 0xFFFF
if x == 0:
x = 0x10000
if y == 0:
y = 0x10000
r = (x * y) % 0x10001
if r == 0x10000:
r = 0
assert 0 <= r <= 0xFFFF
return r
def _KA_layer(x1, x2, x3, x4, round_keys):
assert 0 <= x1 <= 0xFFFF
assert 0 <= x2 <= 0xFFFF
assert 0 <= x3 <= 0xFFFF
assert 0 <= x4 <= 0xFFFF
z1, z2, z3, z4 = round_keys[0:4]
assert 0 <= z1 <= 0xFFFF
assert 0 <= z2 <= 0xFFFF
assert 0 <= z3 <= 0xFFFF
assert 0 <= z4 <= 0xFFFF
y1 = _mul(x1, z1)
y2 = (x2 + z2) % 0x10000
y3 = (x3 + z3) % 0x10000
y4 = _mul(x4, z4)
return y1, y2, y3, y4
def _MA_layer(y1, y2, y3, y4, round_keys):
assert 0 <= y1 <= 0xFFFF
assert 0 <= y2 <= 0xFFFF
assert 0 <= y3 <= 0xFFFF
assert 0 <= y4 <= 0xFFFF
z5, z6 = round_keys[4:6]
assert 0 <= z5 <= 0xFFFF
assert 0 <= z6 <= 0xFFFF
p = y1 ^ y3
q = y2 ^ y4
s = _mul(p, z5)
t = _mul((q + s) % 0x10000, z6)
u = (s + t) % 0x10000
x1 = y1 ^ t
x2 = y2 ^ u
x3 = y3 ^ t
x4 = y4 ^ u
return x1, x2, x3, x4
class IDEA:
def __init__(self, key):
self._keys = None
self.change_key(key)
def change_key(self, key):
assert 0 <= key < (1 << 128)
modulus = 1 << 128
sub_keys = []
for i in range(9 * 6):
sub_keys.append((key >> (112 - 16 * (i % 8))) % 0x10000)
if i % 8 == 7:
key = ((key << 25) | (key >> 103)) % modulus
keys = []
for i in range(9):
round_keys = sub_keys[6 * i: 6 * (i + 1)]
keys.append(tuple(round_keys))
self._keys = tuple(keys)
def encrypt(self, plaintext):
assert 0 <= plaintext < (1 << 64)
x1 = (plaintext >> 48) & 0xFFFF
x2 = (plaintext >> 32) & 0xFFFF
x3 = (plaintext >> 16) & 0xFFFF
x4 = plaintext & 0xFFFF
for i in range(8):
round_keys = self._keys[i]
y1, y2, y3, y4 = _KA_layer(x1, x2, x3, x4, round_keys)
x1, x2, x3, x4 = _MA_layer(y1, y2, y3, y4, round_keys)
x2, x3 = x3, x2
# Note: The words x2 and x3 are not permuted in the last round
# So here we use x1, x3, x2, x4 as input instead of x1, x2, x3, x4
# in order to cancel the last permutation x2, x3 = x3, x2
y1, y2, y3, y4 = _KA_layer(x1, x3, x2, x4, self._keys[8])
ciphertext = (y1 << 48) | (y2 << 32) | (y3 << 16) | y4
return ciphertext
def main():
# key = 0x00000000000000000000000000000000
# plain = 0x8000000000000000
# cipher = 0x8001000180008000
key = 0x2BD6459F82C5B300952C49104881FF48
plain = 0xF129A6601EF62A47
cipher = 0xEA024714AD5C4D84
print 'key\t\t', hex(key)
print 'plaintext\t', hex(plain)
my_IDEA = IDEA(key)
encrypted = my_IDEA.encrypt(plain)
assert encrypted == cipher
print 'ciphertext\t', hex(cipher)
if __name__ == '__main__':
main()