IV. Hoàn Thiện Thuật Toán AES
1. Xây Dựng Hàm Mã Hóa và Giải Mã
Để thực hiện mã hóa sử dụng thuật toán AES, chúng ta có tổng cộng 5 công việc chính: ExpandKey, AddRoundKey, SubBytes, ShiftRows, và MixColumns. Ở phần này, chúng ta sẽ hoàn thiện việc chuyển thể hàm ExpandKey từ lý thuyết sang mã hóa. Đối với AES-128, chúng ta cần ôn lại công thức tính các từ (word) trong quá trình mở rộng khóa:
$$\ W_i = \begin{cases} K_i & \text{nếu } i < 4 \ W_{i-4} \oplus \operatorname{SubWord}(\operatorname{RotWord}(W_{i-1})) \oplus rcon_{i/4} & \text{nếu } i \ge 4 \text{ và } i \equiv 0 (mod 4) \ W_{i-4} \oplus W_{i-1} & \text{Các trường hợp còn lại} \end{cases}$$
Dưới đây là hàm expand_key()
mà chúng ta sẽ áp dụng:
def expand_key(master_key):
r_con = (
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A,
0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A,
0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39,
)
key_columns = bytes2matrix(master_key)
iteration_size = len(master_key) // 4
i = 1
while len(key_columns) < (N_ROUNDS + 1) * 4:
word = list(key_columns[-1])
if len(key_columns) % iteration_size == 0:
word.append(word.pop(0))
word = [s_box[b] for b in word]
word[0] ^= r_con[i]
i += 1
word = bytes(i ^ j for i, j in zip(word, key_columns[-iteration_size]))
key_columns.append(word)
return [key_columns[4 * i : 4 * (i + 1)] for i in range(len(key_columns) // 4)]
Khi xem xét hàm trên, có vài điểm quan trọng cần lưu ý:
r_con
lưu trữ các hằng số vòng tròn mà chúng ta sẽ sử dụng.key_columns
là nơi lưu trữ giá trị của secret key để mở rộng.iteration_size
đại diện kích thước vòng lặp, trong trường hợp này là 4.- Vòng lặp sẽ tiếp tục cho đến khi đạt được số lượng từ cần thiết.
Với việc mở rộng khóa thành công, chúng ta đã chuẩn bị đầy đủ các hàm cần thiết cho AES-128. Tiếp theo, chúng ta cần sắp xếp các hàm theo thứ tự thực hiện:
Bảng trạng thái (state table) là phần trung tâm liên tục thay đổi qua các vòng mã hóa. Chúng ta sẽ sử dụng biến state
để lưu giá trị của bảng trạng thái:
state = bytes2matrix(plaintext)
Công việc đầu tiên là thực hiện AddRoundKey, sử dụng 4 từ đầu của khóa đã mở rộng:
add_round_key(state, round_keys[0])
Trong 10 vòng tiếp theo, chúng ta sẽ thực hiện các công việc SubBytes, ShiftRows, MixColumns và AddRoundKey:
for i in range(1, N_ROUNDS):
sub_bytes(state)
shift_rows(state)
mix_columns(state)
add_round_key(state, round_keys[i])
Đối với vòng cuối cùng, chúng ta thực hiện:
sub_bytes(state)
shift_rows(state)
add_round_key(state, round_keys[10])
Cuối cùng, hàm encrypt_aes()
sẽ như sau:
def encrypt_aes(key, plaintext):
round_keys = expand_key(key)
state = bytes2matrix(plaintext)
add_round_key(state, round_keys[0])
for i in range(1, N_ROUNDS):
sub_bytes(state)
shift_rows(state)
mix_columns(state)
add_round_key(state, round_keys[i])
sub_bytes(state)
shift_rows(state)
add_round_key(state, round_keys[10])
ciphertext = matrix2bytes(state)
return ciphertext
Giải Mã
Để thực hiện giải mã, ta chỉ cần thay đổi thứ tự các công việc:
def decrypt_aes(key, ciphertext):
round_keys = expand_key(key)
state = bytes2matrix(ciphertext)
add_round_key(state, round_keys[10])
for i in range(N_ROUNDS - 1, 0, -1):
inv_shift_rows(state)
inv_sub_bytes(state)
add_round_key(state, round_keys[i])
inv_mix_columns(state)
inv_shift_rows(state)
inv_sub_bytes(state)
add_round_key(state, round_keys[0])
plaintext = matrix2bytes(state)
return plaintext
Hai hàm mã hóa và giải mã trên có thể làm việc với thông điệp có độ dài tối đa 16 ký tự. Nếu thông điệp dài hơn, chúng ta cần tìm hiểu cách xử lý tiếp theo.
2. Sử Dụng Module AES Trong Thư Viện Crypto.Cipher
Như đã đề cập, không phải lúc nào chúng ta cũng cần tự lập trình lại mọi thứ. Trong Python, thư viện Crypto.Cipher cung cấp những công cụ hỗ trợ cho việc mã hóa AES:
from Crypto.Cipher import AES
Dưới đây là ví dụ mã hóa:
data = b'VIBLOCTF{crypto}'
key = b'\xc3,\xa6\xb5\x80^\x0c\xdb\x8d\xa5z*\xb6\xfe\'
cipher = AES.new(key, AES.MODE_ECB)
ciphertext = cipher.encrypt(data)
print(ciphertext)
Và ví dụ giải mã:
key = b'\xc3,\xa6\xb5\x80^\x0c\xdb\x8d\xa5z*\xb6\xfe\'
ciphertext = b'B\xf5\x9d\xb8\xd5\x84\x9a\x19\xcaS\xf6\xd0!d\xf3p'
cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)
print(plaintext)
V. Thử Thách CTF
Chúng ta sẽ xem xét thử thách CTF có tên Passwords as keys. Mã nguồn như sau:
from Crypto.Cipher import AES
import hashlib
import random
with open("/usr/share/dict/words") as f:
words = [w.strip() for w in f.readlines()]
keyword = random.choice(words)
KEY = hashlib.md5(keyword.encode()).digest()
FLAG = ?
@chal.route('/passwords_as_keys/decrypt/<ciphertext>/<password_hash>/')
def decrypt(ciphertext, password_hash):
ciphertext = bytes.fromhex(ciphertext)
key = bytes.fromhex(password_hash)
cipher = AES.new(key, AES.MODE_ECB)
try:
decrypted = cipher.decrypt(ciphertext)
except ValueError as e:
return {"error": str(e)}
return {"plaintext": decrypted.hex()}
@chal.route('/passwords_as_keys/encrypt_flag/')
def encrypt_flag():
cipher = AES.new(KEY, AES.MODE_ECB)
encrypted = cipher.encrypt(FLAG.encode())
return {"ciphertext": encrypted.hex()}
Với các condition trong cách mã hóa, ta cần sử dụng brute force để tìm ra KEY từ danh sách từ để giải mã và tìm FLAG thông qua ứng dụng web. Dưới đây là giải pháp tham khảo:
import requests
import json
import hashlib
from Crypto.Cipher import AES
BASE_URL = 'https://aes.cryptohack.org'
encrypt_flag_path = '/passwords_as_keys/encrypt_flag/'
wordlist_url = 'https://gist.githubusercontent.com/wchargin/8927565/raw/d9783627c731268fb2935a731a618aa8e95cf465/words'
def get_ciphertext(url, path):
r = requests.get(url = url + path)
response = r.text.strip()
data = json.loads(response)
return data['ciphertext']
def decrypt_aes(KEY, ciphertext):
cipher = AES.new(KEY, AES.MODE_ECB)
flag = cipher.decrypt(ciphertext)
return flag
def decrypt(ciphertext):
r = requests.get(wordlist_url)
wordlist = r.text.strip().split()
ciphertext = bytes.fromhex(ciphertext)
for word in wordlist:
KEY = hashlib.md5(word.encode()).digest()
flag = decrypt_aes(KEY, ciphertext)
try:
print(flag.decode())
except:
next
ciphertext = get_ciphertext(BASE_URL, encrypt_flag_path)
decrypt(ciphertext)
Tài Liệu Tham Khảo
source: viblo