# Differential Cryptanalysis on FEAL-4
# 1. Giới thiệu
FEAL (Fast Data Encipherment Algorithm) là một thuật toán mã hoá khối dùng khóa bí mật được thiết kế vào cuối thập kỷ 1980 như một lựa chọn thay thế cho thuật toán mã hóa tiêu chuẩn (DES). Nó được phát triển bởi Akihiro Shimizu và Shoji Miyaguchi của công ty NTT (Nippon Telegraph and Telephone Corporation) ở Nhật Bản. FEAL đã thu hút một số sự chú ý trong cộng đồng mật mã học do cấu trúc và nguyên tắc thiết kế của nó.
FEAL được thiết kế với kích thước khối 64-bit và hỗ trợ độ dài khóa 128 bit. Nó hoạt động trên các khối dữ liệu 32-bit và sử dụng cấu trúc mạng Feistel, một phương pháp thiết kế phổ biến trong các thuật toán mã hoá khối. Mạng Feistel bao gồm việc chia khối dữ liệu thành hai nửa và sau đó thực hiện nhiều vòng xử lý trên các nửa này trước khi kết hợp chúng lại để tạo ra kết quả cuối cùng.
FEAL đã trải qua nhiều phiên bản, với FEAL-4 là phiên bản được nghiên cứu và phân tích nhiều nhất. Nó nổi bật với sự đơn giản và khả năng triển khai hiệu quả trên phần cứng, điều này làm cho nó hấp dẫn đối với một số ứng dụng cụ thể. Tuy nhiên, theo thời gian, các phân tích mật mã đã tiết lộ những yếu điểm và lỗ hổng trong thiết kế của FEAL làm cho nó không an toàn như ban đầu tin.
Một trong những lỗ hổng bảo mật quan trọng nhất tìm thấy trong FEAL-4 liên quan đến phân tích mã hóa khác biệt, một phương pháp được sử dụng để tận dụng sự khác biệt giữa các cặp đầu vào và đầu ra để suy ra thông tin về cách hoạt động bên trong của mật mã. Lỗ hổng này cuối cùng dẫn đến việc làm yếu đi tính bảo mật của FEAL.
Do những lỗ hổng này và việc phát hiện ra các thuật toán mã hoá mạnh hơn, FEAL không được coi là lựa chọn khuyến nghị cho các ứng dụng mật mã học hiện đại. Nó đã được thay thế chủ yếu bởi các mã hoá mạnh hơn như Tiêu chuẩn Mã hoá Nâng cao (AES), đã trải qua quá trình phân tích và kiểm tra cẩn thận để đảm bảo tính bảo mật của nó.
Tóm lại, FEAL là một cố gắng sớm để thiết kế một thuật toán mật mã hóa với mục tiêu là nhanh hơn và hiệu quả hơn so với DES. Tuy nhiên, do các điểm yếu về bảo mật và sự xuất hiện của các thuật toán mạnh hơn, FEAL không còn được coi là lựa chọn an toàn cho việc mã hoá.
# 2. Cấu trúc hoạt động của FEAL-4
- FEAL-4 là một phiên bản cụ thể của thuật toán mã hoá FEAL. Nó hoạt động theo cấu trúc mạng Feistel, một cấu trúc phổ biến trong các thuật toán mã hoá khối. Cấu trúc Feistel chia khối dữ liệu thành hai nửa và sau đó thực hiện nhiều vòng xử lý trên các nửa này trước khi kết hợp chúng lại để tạo ra kết quả cuối cùng. Dưới đây là cách FEAL-4 hoạt động:

- **Key Expansion:** 128-bit key sẽ được đưa vào thuật toán sinh khóa để tạo thành các khóa con(subkeys). Riêng FEAL-4 thì tổng cộng có 6 khóa con
- **Block Division and Combination:** Đầu tiên mỗi block 64-bit được chia thành hai nửa, gọi là left half (L0) và right half (R0). Hai nửa này được xor với khóa con 4 và 5 theo hình ở trên
- **Round Function:** FEAL-4 sử dụng bốn vòng xử lý để biến đổi dữ liệu. Mỗi vòng thực hiện các bước sau:
+ **Subkey XOR:** Trong mỗi vòng, khóa con được XOR với nửa bên phải của dữ liệu (Rn).
+ **F-Function:** Nửa bên phải của dữ liệu (Rn) được đưa vào một F-Function, bên trong là phép XOR của 4 g-funtion nhỏ hơn gồm 3 phép toán là AND,XOR, Circular Bit Shift

```python3=
def rot(a):
return ((a<<2) | (a>>6)) &0xff
def g_box(a, b, mode):
return rot((a + b + mode) & 0xff)
def f_box(tmp:bytes):
assert len(tmp)==4
x = [i for i in tmp]
t0 = x[2] ^ x[3]
y1 = g_box(x[0] ^ x[1], t0, 1)
y0 = g_box(x[0], y1, 0)
y2 = g_box(t0, y1, 0)
y3 = g_box(x[3], y2, 1)
return bytes([y0,y1,y2,y3])
```
+ **XOR with Left Half:** Kết quả của hàm F-Function (F(Rn)) được XOR với nửa bên trái của dữ liệu (Ln) và kết quả được gán cho nửa bên phải mới (Rn+1)
+ **Swap:** Nửa bên phải mới (Rn+1) và nửa bên trái cũ (Ln) được trao đổi vị trí, tức là Rn+1 trở thành Ln+1 và ngược lại
+ **Final Simplification:** Sau khi hoàn thành bốn vòng xử lý, khối dữ liệu đã được biến đổi đến mức độ đủ. Cuối cùng, nửa bên phải và nửa bên trái được gộp lại để tạo ra kết quả mã hoá 64-bit.
- Demo full FEAL-4:
```python3=
from pwn import *
def rot(a):
return ((a<<2) | (a>>6)) &0xff
def g_box(a, b, mode):
return rot((a + b + mode) & 0xff)
def f_box(tmp:bytes):
assert len(tmp)==4
x = [i for i in tmp]
t0 = x[2] ^ x[3]
y1 = g_box(x[0] ^ x[1], t0, 1)
y0 = g_box(x[0], y1, 0)
y2 = g_box(t0, y1, 0)
y3 = g_box(x[3], y2, 1)
return bytes([y0,y1,y2,y3])
class FEAL_4():
def __init__(self, key:bytes):
assert len(key) == 6*4
self.subkeys = []
for i in range(0,6*4,4):
self.subkeys.append(key[i:i+4])
def pad(self, pt: bytes):
num = len(pt) % 8
pt += b'\x00'*(8-num)
return pt
def encrypt_block(self, pt:bytes):
pleft = pt[:4]
pright = pt[4:]
left = xor(pleft, self.subkeys[4])
right = xor(pright, self.subkeys[5])
R2L = xor(left, right)
R2R = xor(left, f_box(xor(R2L, self.subkeys[0])))
R3L = R2R
R3R = xor(R2L, f_box(xor(R2R, self.subkeys[1])))
R4L = R3R
R4R = xor(R3L, f_box(xor(R3R, self.subkeys[2])))
cleft = xor(R4L, f_box(xor(R4R, self.subkeys[3])))
cright = xor(cleft, R4R)
return (cleft + cright)
def decrypt_block(self, ct:bytes):
cleft = ct[:4]
cright = ct[4:]
R4R = xor(cleft,cright)
R4L = xor(cleft, f_box(xor(R4R, self.subkeys[3])))
R3R = R4L
R3L = xor(R4R, f_box(xor(R3R, self.subkeys[2])))
R2R = R3L
R2L = xor(R3R, f_box(xor(R2R, self.subkeys[1])))
left = xor(R2R, f_box(xor(R2L, self.subkeys[0])))
right = xor(left, R2L)
pleft = xor(left, self.subkeys[4])
pright = xor(right, self.subkeys[5])
return (pleft+pright)
def encrypt(self, plaintext:bytes):
pt_pad = self.pad(plaintext)
ct = b''
for i in range(0,len(pt_pad),8):
ct += self.encrypt_block(pt_pad[i:i+8])
return ct
def decrypt(self, ciphertext:bytes):
assert len(ciphertext) % 8 == 0, 'length is incorrect'
pt = b''
for i in range(0,len(ciphertext),8):
pt += self.decrypt_block(ciphertext[i:i+8])
return pt
```
# 3. Cryptanalysis
## a. Finding Deferential Trace
- Điểm yếu của FEAL nằm ở hàm f-box, cụ thể ta thấy rằng:

- Hay nói cách khác ta luôn có:
$$
fbox(x)\oplus fbox(x \oplus 0x80800000)=0x02000000
$$
## b. Exploit

- Gọi lần lượt $a,b,c,d$ là các điểm đánh dấu trong hình vẽ:
- Ta sẽ chia 2 cặp plain-ciphertext là: $(pt,ct)$ và $(pt',ct')$ với $pt' = pt \oplus input(input = 0x8080000080800000)$
- $a = L_0 \oplus K_4 \oplus fbox(R_0 \oplus K_5 \oplus L_0 \oplus K_4 \oplus K_0)$
- $a' = L_0 \oplus 0x80800000 \oplus K_4 \oplus fbox(R_0 \oplus 0x80800000 \oplus K_5 \oplus L_0 \oplus 0x80800000 \oplus K_4 \oplus K_0)$
$\implies a \oplus a' = 0x80800000(1)$
- $b = R_0 \oplus K_5 \oplus L_0 \oplus K_4 \oplus K_0 \oplus fbox(a \oplus K_1)$
- $b' = R_0 \oplus K_5 \oplus L_0 \oplus K_4 \oplus K_0 \oplus fbox(a \oplus K_1 \oplus 0x80800000)$
$\implies b \oplus b' = 0x02000000(2)$
- $c = a \oplus fbox(b \oplus K_2)$
- $c' = a' \oplus fbox(b' \oplus K_2)$
$\implies c \oplus c' = 0x80800000 \oplus fbox(b \oplus K_2) \oplus fbox(b' \oplus K_2)(3)$
- $d = b \oplus fbox(c \oplus K_3)$
- $d' = b' \oplus fbox(c' \oplus K_3)$
$\implies d \oplus d' =0x02000000 \oplus fbox(c \oplus K_3) \oplus fbox(c' \oplus K_3)(4)$
- Ta đã có $c,c',d,d'$
- Ta chia quá trình exploit làm 2 giai đoạn:
- **Giai đoạn 1** (Recover $K_3, K_2, K_1$):
- Đầu tiên ta brute-force $K_3$ tới khi thỏa mãn $(4)\implies (b,b',K_3)$
- Có được $(b,b')$, ta tiếp tục brute-force $K_2$ tới khi thỏa mãn $(3) \implies (a,a',K_2)$
- Do $(2)$ không phụ thuộc vào $K_1$ nên ta sẽ đổi $input = 0x0000000080800000$
- Lúc này $a \oplus a' = 0x02000000$ và $b \oplus b' = 0x80800000 \oplus fbox(a \oplus K_1) \oplus fbox(a' \oplus K_1) \implies K_1$
- **Giai đoạn 2** (Recover K_0, K_4, K_5):
- Ta sẽ brute-force $K_0 \implies (K_4,K_5)$
- So sánh plaintext và ciphertext sẽ cho ra được bộ $(K_0,K_4,K_5)$
`crack_key.py`
```python=
from pwn import *
from Crypto.Util.number import long_to_bytes
from tqdm import tqdm
def rot(a):
return ((a<<2) | (a>>6)) &0xff
def g_box(a, b, mode):
return rot((a + b + mode) & 0xff)
def f_box(tmp:bytes):
assert len(tmp)==4
x = [i for i in tmp]
t0 = x[2] ^ x[3]
y1 = g_box(x[0] ^ x[1], t0, 1)
y0 = g_box(x[0], y1, 0)
y2 = g_box(t0, y1, 0)
y3 = g_box(x[3], y2, 1)
return bytes([y0,y1,y2,y3])
def undo_final_operation(pairs):
for i in range(len(pairs)):
cipher_left_0 = pairs[i][0][1][:4]
cipher_right_0 = xor(pairs[i][0][1][4:],cipher_left_0)
cipher_left_1 = pairs[i][1][1][:4]
cipher_right_1 = xor(pairs[i][1][1][4:],cipher_left_1)
pairs[i][0][1] = cipher_left_0 + cipher_right_0
pairs[i][1][1] = cipher_left_1 + cipher_right_1
return pairs
def undo_last_round(pairs, round_key):
for i in range(len(pairs)):
cipher_left_0 = pairs[i][0][1][:4]
cipher_right_0 = pairs[i][0][1][4:]
cipher_left_1 = pairs[i][1][1][:4]
cipher_right_1 = pairs[i][1][1][4:]
cipher_left_0 = cipher_right_0
cipher_left_1 = cipher_right_1
cipher_right_0 = xor(f_box(xor(cipher_left_0,round_key)), pairs[i][0][1][:4])
cipher_right_1 = xor(f_box(xor(cipher_left_1,round_key)), pairs[i][1][1][:4])
pairs[i][0][1] = cipher_left_0 + cipher_right_0
pairs[i][1][1] = cipher_left_1 + cipher_right_1
return pairs
def crack_round_key(pairs, output_differential):
valid_candidates = []
for candidate_key in tqdm(range(0,2**32,1)):
score = 0
for i in range(len(pairs)):
cipher_left = xor(pairs[i][0][1][:4],pairs[i][1][1][:4])
cipher_right = xor(pairs[i][0][1][4:],pairs[i][1][1][4:])
y = cipher_right
z = xor(cipher_left, output_differential)
y0 = pairs[i][0][1][4:]
y1 = pairs[i][1][1][4:]
candidate_input0 = xor(y0,long_to_bytes(candidate_key))
candidate_input1 = xor(y1,long_to_bytes(candidate_key))
candidate_output0 = f_box(candidate_input0)
candidate_output1 = f_box(candidate_input1)
candidate_differential = xor(candidate_output0 ,candidate_output1)
if (candidate_differential == z):
score += 1
else:
break
if (score == len(pairs)):
valid_candidates.append(long_to_bytes(candidate_key))
return valid_candidates
def phase1(current_round, subkeys = [], chosen_pairs = []):
valid_candidates = []
if current_round == 3:
output_differential = b'\x02\x00\x00\x00'
elif current_round == 2 or current_round == 1:
output_differential = b'\x80\x80\x00\x00'
elif current_round == 0:
return [subkeys[::-1]]
print(f'[*] Cracking round {current_round+1}, using output differential {output_differential} ...')
pairs = undo_final_operation(chosen_pairs[current_round])
for j in range(0,(3-current_round)):
pairs = undo_last_round(pairs, subkeys[j])
candidate_roundkeys = crack_round_key(pairs, output_differential)
if (len(candidate_roundkeys) == 0):
return []
else:
for candidate_k in candidate_roundkeys:
print(f'[*] Trying candidate subkey {candidate_k} for round {current_round+1}...')
r = phase1(current_round-1, subkeys + [candidate_k], chosen_pairs)
if (len(r) > 0):
valid_candidates += r
return valid_candidates
def phase2(candidate_schedules = [], chosen_pairs = [], multi = False):
valid_schedules = []
for subkeys in candidate_schedules:
pairs = undo_last_round(chosen_pairs[1], subkeys[0])
# k0_guess = 0
for k0_guess in tqdm(range(911026000,911027000,1)):
k4_guess = None
k5_guess = None
for j in range(len(pairs)):
plain_left_0 = pairs[j][0][0][:4]
plain_right_0 = pairs[j][0][0][4:]
cipher_left_0 = pairs[j][0][1][:4]
cipher_right_0 = pairs[j][0][1][4:]
y = xor(f_box(xor(cipher_right_0,long_to_bytes(k0_guess))), cipher_left_0)
if k4_guess == None:
k4_guess = xor(y, plain_left_0)
k5_guess = xor(xor(y, cipher_right_0),plain_right_0)
elif (xor(y,plain_left_0) != k4_guess) or (xor(xor(y, cipher_right_0),plain_right_0)!=k5_guess):
k4_guess = None
k5_guess = None
break
if ((k4_guess != None) and (k5_guess != None)):
subkeys.insert(0, long_to_bytes(k0_guess))
subkeys.insert(4, k4_guess)
subkeys.insert(5, k5_guess)
break
if(len(subkeys) == 6):
if (multi):
valid_schedules.append(subkeys)
else:
return [subkeys]
return valid_schedules
def differential_cryptanalysis(chosen_pairs):
subkeys = []
candidate_schedules = phase1(3, subkeys, chosen_pairs)
if (len(candidate_schedules) == 0):
print("[-] Failed to crack round keys 2 to 4...")
return
else:
print(f'[*] Recovered {len(candidate_schedules)} candidate partial key schedules...')
return phase2(candidate_schedules, chosen_pairs, False)
if __name__ == "__main__":
print("~ FEAL-4 Differential Cryptanalysis attack under chosen plaintext conditions ~")
print("based on Jon King's FEAL-4 differential cryptanalysis demo code (http://www.theamazingking.com/feal4full.c)")
pairs2 = [[[b'\x13\xa6\x9b\x16r\xb4\x05\xf4', b',\x1c^lYW&\xb3'], [b'\x13\xa6\x9b\x16\xf24\x05\xf4', b'\x81\x9e\x1dHY\x94\x9b\x8f']], [[b'\xdc\xb2P\xddU\x1dj\x98', b'\xbd\\\x00hB\xd91N'], [b'\xdc\xb2P\xdd\xd5\x9dj\x98', b'\xa5\xf8\\E\xc2\xb3\xe3\xa4']], [[b'6)\xe9\x80k\xb1\x1b!', b'\xa1\xddM\xb3\xb4\xf1\xa7\x14'], [b'6)\xe9\x80\xeb1\x1b!', b"\xdd\x1d\xe7\xf8\xd72\x83'"]], [[b'U\xf0\xeb\x0e\x85\xfbF\x9f', b'\xdc\xed\x83x\x9c\\\x9b\xa1'], [b'U\xf0\xeb\x0e\x05{F\x9f', b'\xf7\xf4\xab\x8b\x14E2V']], [[b'\xf8\xa3\x19l$\x013W', b"r\x87\xce\x01>\xa6'V"], [b'\xf8\xa3\x19l\xa4\x813W', b'\xda\x97,\x08\xfet]?']], [[b'\x8b\xd8\xd2=@\xda\xaea', b'\xf8\xd3\xc0\x01P\xd7H\xcc'], [b'\x8b\xd8\xd2=\xc0Z\xaea', b'\xa2\x08\x96\xe6\xe5y\xdf ']], [[b'q\xf3\x06z\x0e\xc4W\t', b'\x82\x1c\xf4\xde\xb8<\xe9\xd9'], [b'q\xf3\x06z\x8eDW\t', b'\xdc\x88H\\\x83)*G']], [[b'_\xbe\xafE\xc5z\xdbV', b'n4\\p\x95\xb4\xd8h'], [b'_\xbe\xafEE\xfa\xdbV', b'p;\x95\xe7\xab\xeb\x82\x1f']], [[b'\xc2,\xdc\x94\xff\x97*\x97', b'cf\x85|b\xe5 q'], [b'\xc2,\xdc\x94\x7f\x17*\x97', b';~I\r\xd6\xb2U ']], [[b'~&\xbch#\xe0v\xa7', b'\x14\xa8\x91T\xd6\x17\\\xf7'], [b'~&\xbch\xa3`v\xa7', b'\xee\xdc$\x1c\x9cgEr']], [[b'CC\x88\xfed\xfa\xa5\xe9', b'K9hJLqo\xbb'], [b'CC\x88\xfe\xe4z\xa5\xe9', b'\x80\x19\xfac%\xcd=R']], [[b'\xc4{\xa8JA)$1', b'\x18\xbf0\xe1\xb1\x9f\x8a\xf1'], [b'\xc4{\xa8J\xc1\xa9$1', b'\xe8\xb6sM\xf8\x94Rq']]]
pairs3 = [[[b"\xa6\x14'\x8b\x971\xa6\xc7", b'{PM\xcb\xed\xc9r\xda'], [b"&\x94'\x8b\x17\xb1\xa6\xc7", b'\x08\xd4\xf9\n\xc6\xd5%\x9f']], [[b'\xd3N!~\x0bo\xab~', b'h\x96\xac\xd6J\n\x14\xe4'], [b'S\xce!~\x8b\xef\xab~', b'f1Y\xc1\xe8\xd4\xc5c']], [[b'\x04w\xfbd\xea\x1ag,', b'\x87\x08\x8e\xcc&\xe7\xa7\x95'], [b'\x84\xf7\xfbdj\x9ag,', b'\x0b\x000K\xc3g9\x93']], [[b'\x84c\x98\x9e\xe6+\x90\xcc', b'\x1f\xb3\xe9\xd6Zd\x0b\xfb'], [b'\x04\xe3\x98\x9ef\xab\x90\xcc', b'\xab\xb0t\xeaF\xefvG']], [[b"\x1b\xa9\xcb,'\xd6\xb7\xd4", b'\xd4"\xcc\xa4\x02=\x96E'], [b'\x9b)\xcb,\xa7V\xb7\xd4', b'\xbf\xe0U\xb8\xf1w\xef\xd9']], [[b'=\xac@Kn\xc18\xe6', b'\xb4\xb4\x8dC\x0e\x81\x0c\xfd'], [b'\xbd,@K\xeeA8\xe6', b'Y\xc2%\xe3\x88o\x84\xdd']], [[b'o\xee\x8fI\xc2YH\x16', b'\x02Gr\xea\xb7\xdbK\xec'], [b'\xefn\x8fIB\xd9H\x16', b'\xe9\x81\xe5+\xc4\x95\xfc\xae']], [[b'\xb8\xa1\xfei\xd2\x04\xe0r', b'\x8c\xf0v\xb2\xfb\xb46\x9d'], [b'8!\xfeiR\x84\xe0r', b'>,\xea\xee\xe1\xf0\xca@']], [[b'\xf0?lhe\xbfZv', b'\xff1\xa1w\xfd^\xfeU'], [b'p\xbflh\xe5?Zv', b'\x15s*\x1b\xaf\x94U\xb9']], [[b'Z\x8cAr\xd1\xa5{~', b'I^\xcc*\x84%\xac$'], [b'\xda\x0cArQ%{~', b'%\x1fB2@\xfc\xc2\xbc']], [[b'#\xf1-\x82?\x83\xe7Z', b'\xfb\x90\x0c_\xc6\xbept'], [b'\xa3q-\x82\xbf\x03\xe7Z', b'I\xc9\x94#\x1f_\x08\x8b']], [[b'\xc3\xcdH@\xf4\xb5\x8e\t', b'?\xfe\x06\xa6#+\xd18'], [b'CMH@t5\x8e\t', b'\xa5\xfc\x8e\x86\xd1\xa19\x98']]]
pairs4 = [[[b"'\x9fM]\xa47\xc9\x9c", b'\x01k\x19\x16c]Hz'], [b'\xa7\x1fM]$\xb7\xc9\x9c', b's\xa7\xa7\x1b\xb9\t\x96\xf4']], [[b'e\\[&\xfc\xcc_W', b'\xf2f\xcb\xaa\xe2\xcb=\xa7'], [b'\xe5\xdc[&|L_W', b'MeU\xe2\x85\xb0\xc3o']], [[b"\x82&'G\xf1D\x8b3", b';\xdf53g-Ox'], [b"\x02\xa6'Gq\xc4\x8b3", b'\x10\xda\x83\xeb\x17\x90\xd9 ']], [[b'DG\x9eW\xc7\xefeF', b'\x18\xd6*n\x1e@\x0b\xbc'], [b'\xc4\xc7\x9eWGoeF', b'C\x98\xbe\xa9\xdd\x86\xbf\xf8']], [[b'\xd3t:E\xa9\x90\xda\xb2', b'q\xfc\xad;\x85+/\xb6'], [b'S\xf4:E)\x10\xda\xb2', b'\x99::[\xc5u\xd8U']], [[b'\x9b %~\x82H\xc3\x88', b'\xd5\xb0c\xd9J)\x87F'], [b'\x1b\xa0%~\x02\xc8\xc3\x88', b'\nm\xf2\x18=l6\x04']], [[b'\xd1+=aJ\xd4=\xbb', b'\xf2 \xd7\xbb\x93?%7'], [b'Q\xab=a\xcaT=\xbb', b'\xde\xb1\xcaY\xd76\xdbB']], [[b'\x13\x1a\x82\x80b\xb8|(', b'\xe5\xf6\x18\x08\x9e\x99k\x97'], [b'\x93\x9a\x82\x80\xe28|(', b'Xw\xa5\x14[\xe0\xb6\x0b']], [[b'\xcco|5\xbe\x1e\xe5\xf6', b'xAra\x97\x0b`\xad'], [b'L\xef|5>\x9e\xe5\xf6', b'\x90\xf4\xfe\x05\x06\x06\x0fF']], [[b'\xc1L\xb6\xb8\xdc\xad\xb3\x9b', b'\xf0!R\x801\xfd\x08\xd3'], [b'A\xcc\xb6\xb8\\-\xb3\x9b', b'9/\x1f\xef K\xa48']], [[b'm\xc4\xa0\x06c\xf99\xc4', b's)\xd8J]mq\x81'], [b'\xedD\xa0\x06\xe3y9\xc4', b'\xdejO.X\xb6\x06e']], [[b'\x19\x0b\xe1\x1d1\x8e\xe9\xc0', b'"2Wi\x830X\xc0'], [b'\x99\x8b\xe1\x1d\xb1\x0e\xe9\xc0', b'z,\xc0!c\xb6\xef\t']]]
valid_schedules = differential_cryptanalysis([None, pairs2, pairs3, pairs4])
key = b''
if (len(valid_schedules) > 0):
for subkeys in valid_schedules:
print(f'[+] Recovered valid FEAL-4 key schedule {subkeys[0]},{subkeys[1]},{subkeys[2]},{subkeys[3]},{subkeys[4]},{subkeys[5]}')
print(valid_schedules)
for i in valid_schedules[0]:
key += i
print("key: ", key)
else:
print("[-] Failed to recover FEAL-4 key schedule...")
```
# 4. Reference
- http://theamazingking.com/crypto-feal.php
- https://samvartaka.github.io/cryptanalysis/2016/04/08/volgactf-fiveblocks-writeup