-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathfeistel.py
87 lines (63 loc) · 2.53 KB
/
feistel.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
import hashlib
""" Class that implements a Feistel Cipher in ECB mode
* 64-byte / 512-bit block size
* SHA256 as a PRF
* PBKDF2_HMAC as a key schedule
"""
class FeistelNetwork():
def __init__(self, key):
self.round_count = 10
self.block_size = 64 # 512 bits
self.keys = self.generate_round_keys(key, self.round_count, self.block_size)
def generate_round_keys(self, key, round_count, block_size):
half_block_size = block_size // 2
# Generate a half block sub-key for every round
# + 2 blocks for the whitening before and after
key_data = hashlib.pbkdf2_hmac('sha256', key, b'Computerphile', 500, dklen = (4 + round_count) * half_block_size)
# [Prekey: 64] [R x Subkeys: 32] [Postkey: 64]
keys = {
"prekey" : key_data[0:block_size],
"roundkeys" : [key_data[block_size + (half_block_size * a): block_size + half_block_size + (half_block_size * a)] for a in range(round_count)],
"postkey" : key_data[-block_size:],
}
return keys
def _xor(self, a, b):
a_i = int.from_bytes(a, "little")
b_i = int.from_bytes(b, "little")
return (a_i ^ b_i).to_bytes(len(a), "little")
def _reverse(self, block):
L = block[:self.block_size//2]
R = block[self.block_size//2:]
return R+L
def round(self, block, round_key):
L = block[:self.block_size//2]
R = block[self.block_size//2:]
Rprime = self._xor(R, round_key)
sha256 = hashlib.sha256()
sha256.update(Rprime)
Rprime = sha256.digest()
L = self._xor(L,Rprime)
return R + L
def encrypt_block(self, block):
# Key whitening
block = self._xor(block, self.keys["prekey"])
# Rounds
for i in range(self.round_count):
block = self.round(block, self.keys["roundkeys"][i])
# Reverse half-block
block = self._reverse(block)
# Key whitening
block = self._xor(block, self.keys["postkey"])
return block
def decrypt_block(self, block):
# Decrypt is identical except the key order is reversed
# Key whitening
block = self._xor(block, self.keys["postkey"])
# Rounds
for i in range(self.round_count - 1, -1, -1):
block = self.round(block, self.keys["roundkeys"][i])
# Reverse half-block
block = self._reverse(block)
# Key whitening
block = self._xor(block, self.keys["prekey"])
return block