0
0
Lập trình
Admin Team
Admin Teamtechmely

Hướng Dẫn Chi Tiết Về Mật Mã Đối Xứng AES Phần 5: Xây Dựng Thuật Toán Mã Hóa và Giải Mã

Đăng vào 3 tuần trước

• 5 phút đọc

Chủ đề:

Cryptoaes

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:

Copy
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:

Copy
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:

Copy
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:

Copy
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:

Copy
sub_bytes(state)
shift_rows(state)
add_round_key(state, round_keys[10])

Cuối cùng, hàm encrypt_aes() sẽ như sau:

Copy
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:

Copy
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:

Copy
from Crypto.Cipher import AES

Dưới đây là ví dụ mã hóa:

Copy
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ã:

Copy
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:

Copy
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:

Copy
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

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào