---
# System prepended metadata

title: Phần 1. Mật mã DES và AES

---

# Phần 1. Mật mã DES và AES
## I. Mật mã DES
### 1. Feistel cipher structure
Trước khi vào nội dung chính của DES, chúng ta hãy tìm hiểu trước về cấu trúc **Feistel network** là một cấu trúc quan trọng trong hệ mã DES.
![image](https://hackmd.io/_uploads/ry-tUG0Rlx.png)
Quy trình này hoạt động một cách lặp đi lặp lại qua nhiều vòng (rounds) để biến đổi bản rõ (plaintext) thành bản mã (ciphertext).
#### a. Qui trình mã hóa.
* **Bước 1: Khỏi tạo.**
 Khối bản rõ đầu vào (ví dụ: 64 bit) được chia thành hai nửa bằng nhau: nửa Trái ($L_i$) và nửa Phải ($R_i$), mỗi nửa có kích thước 32 bit.
* **Bước 2: Xử lý vỏng lập.**
Quá trình này gồm nhiều vòng lập tuần tự ( 16 vòng trong DES ):
    +  Nữa trái mới $L_i$ sẽ bằng với nữa phải cũ $R_{i-1}$.
    +  Nữa phải cũ $R_{i-1}$ sẽ được đưa vào hàm $F$ cùng với một khóa $k_i$ duy nhất của vòng lập đó. Kết quả đầu ra của hàm $F(R_{i-1}, k_i)$ sẽ được XOR với nữa trái cũ $L_{i-1}$ tạo thành nữa phải mới $R_i$.
    + Tổng quát: ![image](https://hackmd.io/_uploads/HkFvcM0Cex.png)
* **Bước 3: Hoán đổi.**
    + Sau khi hoàn thành vòng cuối cùng (ví dụ: vòng 16 tạo ra nữa trái và nữa phải cuối cùng), một thao tác hoán vị được thực hiện: nửa Trái và nửa Phải được tráo đổi vị trí cho nhau.
    + Kết quả của lần hoán vị này chính là bản mã cuối cùng.
#### b. Qui trình giải mã.
Một trong những ưu điểm lớn nhất của cấu trúc Feistel là thuật toán giải mã gần như giống hệt thuật toán mã hóa. Đặc tính này mang lại hiệu quả cao trong việc triển khai cả về phần cứng lẫn phần mềm, vì phần lớn logic có thể được tái sử dụng. 
* Bản mã ( cipher text ) được sử dụng làm input của quá trình giải mã. Khối bản mã này sẽ được chia làm hai nữa trái phải độ dài 32 bit.
* Cấu trúc vòng lập tương tự như mã hóa chỉ khác ở chỗ vòng lập đầu tiên sử dụng khóa $k_{16}$, vòng lập thứ 2 sử dụng khóa $k_{15}$, tương tự như vậy cho đến vòng lập cuối cùng sẽ sử dụng khóa $k_1$.
* Sau khi hoàn thành 16 vòng lập, sẽ hoán đổi vị trí của 2 nữa để phục hồi lại plaintext.

### 2. DES
#### a. Thông số
DES được xây dựng dựa trên cấu trúc Feistel.
**Input:** Khối plaintext 64 bits
**Output:** khối ciphertext 64 bits
**Main key:** 64 bits
**Subkey:** 56 bits
**Round key:** 48 bits
**Số vòng lặp:** 16
#### b. Quá trình mã hóa
![image](https://hackmd.io/_uploads/BkG0zBARxe.png)
Được chia làm 4 giai đoạn:
* **Giai đoạn 1: Hoán vị ban đầu (Initial Permutation - IP).**
Bước đầu tiên lấy khối plaintext 64-bits và xáo trộn theo một bảng hoán vị cố định (IP). kết quả đầu ra vẫn là một khối 64 bits.
![image](https://hackmd.io/_uploads/BJwBDN1J-l.png)
> bit đầu ra số 1 sẽ là bit thứ 58 trong plaintext, bit đầu ra số 2 sẽ bit thứ 50 trong plaintext,..., bit đầu ra cuối cùng chính là bit thứ 7 trong plaintext.
* **Giai đoạn 2: 16 vòng lặp mã hóa.**
    Đây chính là "trái tim" của thuật toán DES, nơi khối dữ liệu 64-bit từ giai đoạn trước sẽ lần lượt đi qua 16 vòng lặp xử lý giống hệt nhau.
    Mỗi vòng lặp (từ 1 đến 16) nhận đầu vào là một khối 64-bit và sử dụng một "khóa vòng" (round key) 48-bit riêng biệt (K1, K2, ..., K16) để biến đổi dữ liệu.
    Đầu ra của mỗi vòng lặp là một khối 64-bit mới, được chuyển tiếp cho vòng lặp kế tiếp.
* **Giai đoạn 3: Hoán đổi 32 bits (swap function)**.
    ◦ Sau khi hoàn thành vòng lặp thứ 16, khối 64-bit kết quả được chia thành hai nửa: 32 bit bên trái và 32 bit bên phải.
    ◦ Một hành động đơn giản được thực hiện: hai nửa này được hoán đổi vị trí cho nhau.
* **Giai đoạn 4: Hoán vị cuối cùng (Final Permutaion - $IP^{-1}$)**.
    Đây là bước cuối cùng, áp dụng một phép hoán vị là nghịch đảo của "Hoán vị ban đầu" (IP) ở Giai đoạn 1.
    Nó sắp xếp lại các bit về cấu trúc cuối cùng, hoàn tất quá trình mã hóa.
    ![image](https://hackmd.io/_uploads/SksZ3N1kWe.png)
> bit đầu tiên chính là bit thứ 40 của plaintext sau 16 vòng lặp, tương tự như vậy cho đến bit cuối cùng.

    Đầu ra của giai đoạn này chính là khối ciphertext 64-bit hoàn chỉnh.

### 3. Thuật toán trong mỗi vòng lặp của DES.
![image](https://hackmd.io/_uploads/rJqIfrk1Wx.png)

Nữa phải cũ sẽ được dùng để làm đầu vào của hàm $F(R_{i-1, k_i})$, đầu ra của hàm sẽ là một khối văn bản 32 bits mới.

#### **- The F Function (Mangler Function)**
![image](https://hackmd.io/_uploads/rJvxISJkWg.png)
* **Giai đoạn 1: Khổi 32 bits đi qua hàm hoán vị mở rộng (Expansion Permutation)**.
    + Khối 32 bits sẽ được mở rộng thành khối 48 bits.
    + Cơ chế: Quá trình mở rộng không tạo ra thông tin mới mà sao chép và sắp xếp lại các bit hiện có. Các bit đầu vào được chia thành các nhóm và một số bit được lặp lại để tạo thêm 16 bit, nâng tổng số từ 32 lên 48 bit.
    ![image](https://hackmd.io/_uploads/B1L_sH1JZx.png)

* **Giai đoạn 2: Phép toán XOR với khóa vòng.**
    Sau khi mở rộng thành khối 48 bits, sẽ lấy khối đó XOR từng bit với khóa vòng 48 bits. Tạo ra một khối 48 bits mới.
* **Giai đoạn 3: Hộp thay thế (Substitution Boxes - S-Boxes).**
    + Đây là giai đoạn cốt lõi và là bước phi tuyến duy nhất trong hàm $F$. Kết quả 48 bit từ phép XOR được nén xuống 32 bit bằng cách sử dụng một bộ tám S-box.
    + Đầu vào: 48 bit.
    + Cấu trúc: Có tám S-box khác nhau (S1 đến S8). Dữ liệu 48 bit được chia thành tám khối 6 bit. Mỗi khối 6 bit được đưa vào một S-box tương ứng.
    + Hoạt động: Mỗi S-box nhận một đầu vào 6 bit và tạo ra một đầu ra 4 bit dựa trên một bảng tra cứu được xác định trước.
![image](https://hackmd.io/_uploads/rkclprJybl.png)
    > Cơ chế: **Xác định Hàng:** Bit đầu tiên và bit thứ sáu của khối 6 bit được kết hợp để tạo thành một số 2 bit, xác định số hàng (từ 0 đến 3) trong bảng S-box. **Xác định Cột:** Bốn bit ở giữa (bit thứ 2, 3, 4, 5) được kết hợp để tạo thành một số 4 bit, xác định số cột (từ 0 đến 15). **Tra cứu Giá trị:** Giá trị nằm ở giao điểm của hàng và cột đã xác định trong bảng S-box chính là đầu ra. Giá trị này là một số có giá trị từ 0 đến 15, được biểu diễn dưới dạng 4 bit.
* **Giai đoạn 4: Hộp hoán vị (Permutatuion Box - P-box).**
Hộp P-box xáo trộn hoặc sắp xếp lại vị trí của 32 bit đầu vào theo một quy tắc cố định. Nó không thay đổi giá trị của các bit, chỉ thay đổi thứ tự của chúng. và tạo ra đầu ra 32 bit.
![image](https://hackmd.io/_uploads/B1dZkUky-l.png)
    > bit đầu tiên của đầu ra chính là bit số 16 của khối 32 bit sau khi đi qua S-Boxes, tương tự cho đến bit cuối cùng.
    
$\implies$ Sau khi hoàn thành vòng lặp. Lấy kết quả từ hàm $F$ XOR với nữa trái cũ để tạo thành nữa phải mới $R_i = L_{i-1} \oplus F(R_{i-1}, K_i).$ Nửa trái mới sẽ là bản sao của nữa phải cũ $L_i = R_{i-1}$.

$\implies$ Sau khi hoàn thành 16 vòng lặp sẽ tiến hành hoán đổi vị trí của nữa trái và nữa phải, kết hợp 2 khối tạo thành khối 64 bits, sau đó áp dụng hoán vị nghịch đảo và tạo ra khối ciphertex.

### 4. Thuật toán tạo khóa vòng $(K_i)$.
![image](https://hackmd.io/_uploads/SJozQskyWx.png)

#### **Bước 1: khởi tạo khóa hiệu dụng (Permuted choice 1 - PC1).**

Khối khóa 64 bits sẽ bị loại bỏ các bits chẵn lẻ tạo thành khóa hiệu dụng 56 bits.
Các bit bị loại bỏ: bit thứ 8, 16, 24, 32, 40, 48, 56, 64.
![image](https://hackmd.io/_uploads/S1_9Sj1yWx.png)
Khối 56 bits được chia làm hai nữa bằng nhau là C0 (28 bit đầu tiên) và D0 (28 bit còn lại). C0 và D0 là trạng thái khởi tạo cho các bước dịch vòng tiếp theo trong quá trình sinh khóa.
#### **Bước 2: Vòng lặp tạo 16 khóa vòng**
Bước này sẽ tạo ra 16 khóa vòng riêng biệt, mỗi lần lặp bao gồm 2 thao tác là dịch vòng trái (Left Circular Shift) và hoán vị nén khóa (Permuted Choice 2 - PC-2).
* **Dịch vòng trái (Left Circular Shift).**
    + Trong mỗi vòng, các bit trong C và D được dịch vòng sang trái một các độc lập. Khi dịch vòng, bit ngoài cùng bên trái sẽ di chuyển sang vị trí ngoài cùng bên phải VD: "10110" -> "01101".
    + số lượng bit dịch không cố định mà thay đổi tùy thuộc vào số vòng lập ( từ 1 đến 16), được quy định trong một bảng cụ thể.
    ![image](https://hackmd.io/_uploads/B14QsoJy-g.png)
* **Hoán vị nén khóa (Permuted choice - PC-2)**.
    + Sau khi thực hiện dịch vòng trái cho mỗi vòng lặp thứ $i$, hai nửa $C_i$ và $D_i$ (mỗi nữa 28 bits) được ghép lại với nhau thành một khối 56 bits, đây là đầu vào cho bước hoán vị nén PC-2.
    + PC-2 nhận đầu vào khối 56 bits và dựa trên một bảng hoán vị cố định, chọn và sắp xếp lại vị trí của 48 bits. kết quả tạo thành một khóa vòng $K_i$.
    ![image](https://hackmd.io/_uploads/HyrApj1yWx.png)

### 5. Giải mã trong DES
Thuật toán giải mã gần như giống hệt thuật toán mã hóa.
* **Đầu vào và Đầu ra:** Vai trò bị đảo ngược. Trong giải mã, đầu vào là bản mã (ciphertext) 64-bit và đầu ra mong muốn là văn bản rõ (plaintext) 64-bit ban đầu.
* **Cấu trúc Thuật toán:** Cấu trúc tổng thể của thuật toán, bao gồm Hoán vị Ban đầu $(IP)$, 16 vòng xử lý, hoán đổi 32-bit, và Hoán vị Nghịch đảo Ban đầu $(IP^{-1})$ vẫn được giữ nguyên. Bản mã được đưa vào Hoán vị Ban đầu (IP) đầu tiên. Lý do là vì trong bước mã hóa cuối cùng, dữ liệu đã đi qua $IP^{-1}$; do đó, để đảo ngược quá trình, bước đầu tiên của giải mã phải là $IP$, vốn là phép nghịch đảo của $IP^{-1}$.
* Giải mã sẽ áp dụng thứ tự các khóa ngược lại so với mã hóa.
![image](https://hackmd.io/_uploads/HybY1nJ1Zx.png)

## II. Các chế độ trong mật mã khối.
### 1. ECB mode (Electronic Codebook).
![image](https://hackmd.io/_uploads/S1i5kwekbx.png)
**Nguyên lý:** Plaintext chia thành từng khối (ví dụ 64 bit), mỗi khối được mã hóa độc lập bằng cùng một khóa. Ciphertext = nối kết quả từng khối.
- **Ưu điểm:**
    + Đây là chế độ đơn giản nhất, không yêu cầu vector khởi tạo (IV).
    + Mỗi khối mã hóa độc lập, tận dụng tối đa xử lý đa lõi tăng tốc độ mã hóa.
    + Các khối mã độc lập với nhau. Vì vậy, khi một khối nào đó có lỗi sẽ không lan truyền đến khối khác.
- **Nhược điểm:**
    + các khối bản rõ giống nhau sẽ tạo ra các khối bản mã giống hệt nhau, làm lộ cấu trúc dữ liệu. 
    + Kẻ tấn công có thể cắt, chèn, hoặc sắp xếp lại các khối bản mã mà không làm hỏng toàn bộ thông điệp.

**Lưu ý:** ECB (Electronic Codebook): Chế độ này mã hóa từng khối 8 byte một cách độc lập. Nếu khối cuối cùng của bạn chỉ có 5 byte, nó phải được đệm (pad) thêm 3 byte nữa để trở thành 8 byte trước khi mã hóa.
**Ứng dụng:** Đây là chế độ dùng cho học thuật, test, debug. Không dử dụng cho dữ liệu thực tế.
### 2. CBC mode (Cipher block chaining).
![image](https://hackmd.io/_uploads/BytIzDe1Wg.png)
**Nguyên lý:** Mỗi khối plaintext được XOR với ciphertext của khối trước đó rồi mới mã hóa. Khối đầu tiên XOR với IV (Initialization Vector).
**Công thức:** $C_i =E_k(P_i \oplus C_{i-1})$ với $C_0 = IV$.
- **Ưu điểm:**
    + Che giấu mẫu dữ liệu: Các khối bản rõ giống nhau sẽ tạo ra các khối bản mã khác nhau, làm cho việc phân tích tần suất trở nên vô ích.
    + Tạo hiệu ứng thác đổ: Một thay đổi nhỏ ở một bit trong bản rõ sẽ ảnh hưởng đến tất cả các khối bản mã tiếp theo, tăng cường sự khuếch tán và làm cho bản mã trở nên khó dự đoán.
    +  sử dụng IV đảm bảo rằng cùng một bản rõ sẽ tạo ra các bản mã khác nhau mỗi lần mã hóa, ngăn chặn các cuộc tấn công phát lại (replay attacks).
- **Nhược điểm:**
    + Mã hóa tuần tự: Quá trình mã hóa không thể song song hóa do mỗi khối phụ thuộc vào khối bản mã trước đó.
    + Một lỗi bit trong khối bản mã Cᵢ sẽ làm hỏng hoàn toàn khối bản rõ Pᵢ và gây lỗi tương ứng trong khối Pᵢ₊₁. Lỗi tự sửa sau 2 khối.

**Lưu ý:** CBC (Cipher Block Chaining): Chế độ này cũng xử lý từng khối 8 byte (mỗi khối phụ thuộc vào khối trước đó). Tương tự như ECB, khối cuối cùng phải được đệm cho đủ 8 byte.
**Ứng dụng:** Phổ biến trong SSL/TLS cũ, IP sec.
### 3. OFP mode (Output Feedback).
![image](https://hackmd.io/_uploads/BJQI2YlyZe.png)
**Nguyên lý:** Đầu tiên, lấy IV làm input block 1 và mã hóa nó với key tạo ra output block 1, lấy output block 1 làm input cho block 2, tương tự như vậy cho đến block n. Output của mỗi block sẽ được XOR với plaintext tương ứng tạo ra ciphertext.
- **Ưu điểm:**
    + Biến mã thành luồng: phù hợp cho dữ liệu truyền tải liên tục.
    + Có thể tạo keystream trước khi có bản rõ, giúp tối ưu hiệu suất.
    + Lỗi bit trong bản mã chỉ ảnh hưởng đến bit tương ứng trong bản rõ, không lan truyền.
- **Nhược điểm:**
    + Lỗi bit trong bản mã chỉ ảnh hưởng đến bit tương ứng trong bản rõ, không lan truyền.
    + Không cung cấp tính toàn vẹn hay xác thực dữ liệu.
    + Dễ bị tấn công thay đổi bit nếu không có cơ chế xác thực riêng.

**Ứng dụng:** tốc độ mã hóa chậm nên ít được sử dụng ở hiện tại.
### 4. CFB mode (Cipher Feedback).
![image](https://hackmd.io/_uploads/r112kcg1Zl.png)
**Nguyên lý:** IV được sử dụng để làm input cho khối đầu tiên, Output của khối đầu tiên được sử dụng để XOR với plaintext tương ứng, tạo thành ciphertext. Sau đó sử dụng ciphertext ở khối đầu tiên làm input cho khối thứ 2, tạo thành một luồng mã hóa cho đến khối thứ n.
- **Ưu điểm:**
    + Xử lý dữ liệu không cần chia khối:  CFB có thể xử lý dữ liệu theo từng bit hoặc byte (`s` bits), loại bỏ nhu cầu đệm (padding)
    + Tự đồng bộ:  Một lỗi bit trong bản mã chỉ ảnh hưởng đến việc giải mã khối hiện tại và một vài khối sau đó. Sau khi bit lỗi được "dịch chuyển" ra khỏi thanh ghi, hệ thống sẽ tự động đồng bộ lại, giúp chế độ này mạnh mẽ hơn trong môi trường truyền không ổn định.
- **Nhược điểm:**
    + Tốc độ chậm do mã hóa tuần tự không thể song song hóa.

**Ứng dụng:** Do tốc độ chậm và nhược điểm về lan truyền lỗi, CFB hiện nay hầu như không còn được sử dụng trong các giao thức hiện đại
### 5. CTR mode (Counter).
![image](https://hackmd.io/_uploads/HkmZV5lJZl.png)
**Nguyên lý:** Sử dụng couter làm input cho mỗi khối tương ứng, sau đó lấy input XOR với plaintext tương ứng tạo thành ciphertext. Lưu ý: Bộ điểm (counter) phải duy nhất, Thay vì mã hóa bản rõ, CTR mã hóa một bộ đếm (counter) tăng dần. Mỗi khối dữ liệu tương ứng với một giá trị bộ đếm duy nhất. Để đảm bảo tính duy nhất, bộ đếm thường được kết hợp với một Nonce (số dùng một lần) - giá trị không cần bí mật, chỉ cần không lặp lại với cùng một khóa.
![image](https://hackmd.io/_uploads/H1S8vcxkZg.png)
- **Ưu điểm:**
    + Tốc độ nhanh và song song hóa, có thể mã hóa và giải mã nhiều khối cùng lúc.
    + Không lan truyền lỗi, các khối độc lập với nhau.
- **Nhược điểm:**
    + Nếu bộ đếm bị lặp, bảo mật bị phá vỡ:  Đây là lỗ hổng nghiêm trọng nhất. Nếu cặp (Nonce, Counter) được tái sử dụng với cùng một khóa, kẻ tấn công có thể thực hiện tấn công "Two-time Pad" bằng cách XOR hai bản mã, từ đó làm lộ thông tin về các bản rõ gốc.

**Ứng dụng:** Ứng dụng rộng rãi và được sử dụng nhiều hiện nay. Lý tưởng cho các ứng dụng cần tốc độ xử lý nhanh như mã hóa tệp lớn, luồng video, và cơ sở dữ liệu quy mô lớn.
### 6. EAX mode
![image](https://hackmd.io/_uploads/Sy1PDyzJ-l.png)
**Nguyên lý:** EAX kết hợp CTR mode (Counter Mode) để mã hóa dữ liệu và OMAC (One-key MAC) để xác thực.
* Đầu vào:
    + (N) nonce: số ngẫu nhiên và duy nhất mỗi lần mã hóa.
    + (M) message: thông điệp cần mã hóa.
    + (H) header: dữ liệu cần xác thực nhưng không mã hóa.
* Tạo OMAC cho từng phần:
    + $OMAC^0_k(N)$: Xác thực nonce
    + $OMAC^1_k$(H): xác thực header
    + $CTR_k(N^`)$: mã hóa message M thành ciphertext C.
+ Xác thực ciphertext:
    + $OMAC^2_k(C)$: Xác thực phần mã hóa
+ Tạo tag xác thực cuối:
    + $T = OMAC^0_k(N) \oplus OMAC^1_k(H) \oplus OMAC^2_k(C)$ 

[Tham khảo trên wikipedia](https://en.wikipedia.org/wiki/EAX_mode).
### 7. Chương trình mã hóa và giải mã DES (CTR mode) đơn giản.
Sử dụng thư viện **Pycryptodome** [Document](https://pycryptodome.readthedocs.io/en/latest/src/cipher/des.html)
```python=
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
from secrets import token_bytes

def encrypt_DES_CTR(key, nonce, msg):
    cipher = DES.new(key, DES.MODE_CTR, nonce= nonce)
    ciphertext = cipher.encrypt(msg)

    return ciphertext

def decrypt_DES_CTR(key, nonce, ciphertext):
    decipher = DES.new(key, DES.MODE_CTR, nonce = nonce)
    decrypted_plaintext = decipher.decrypt(ciphertext)

    return decrypted_plaintext

def main():
    try:
        key = get_random_bytes(8)
    except ImportError:
        key = token_bytes(8)

    try:
        nonce = get_random_bytes(4)
    except ImportError:
        nonce = token_bytes(4)

    message = input("Nhập tin nhắn cần mã hóa: ")
    message = message.encode('utf-8')

    print("-"*50)
    print(f"Plantext(hex): {message.hex()}")
    print(f"Key: {key.hex()}")
    print("-"*50)

    ciphertext = encrypt_DES_CTR(key, nonce, message)
    print("--Decrypting....")
    print(f"Ciphertext: {ciphertext.hex()}")
    print(f"Nonce: {nonce.hex()}")

    decrypted_msg = decrypt_DES_CTR(key, nonce, ciphertext)
    print("--Encrypting....")
    print(f"Decrypted: {decrypted_msg.decode()}")
    print("-"*50)

    assert decrypted_msg == message
    print("Xác minh thành công!")

if __name__ == '__main__':
    main()
```
## III. Mật mã AES.
### 1. Nền tảng toán học.
#### **a. Nhóm (Group)**
**Định nghĩa:** Một nhóm là một tập hợp $G$ gồm những phần tử với phép toán $*$ mà sự kết hợp của hai phần tử bất kì trong G với phép toán $*$ thỏa mãn các điều kiện bên dưới. 
**Kí hiệu**: $(G,*)$
**1. Phép toán $*$ trong nhóm là đóng:** $\forall a,b \in G$ sao cho $a*b=c$ mà $c\in G$.
**2. Có tính kết hợp:** $a*(b*c)=(a*b)*c$ mà $\forall a,b,c \in G$.
**3. Có phần tử đơn vị (trung hòa):** $\forall a \in G, \exists e \in G$ sao cho $a*e=e*a=a$.
**4.Có phần tử nghịch đảo:** $\forall a \in G, \exists a^` \in G$ sao cho $a*a^` = a^` * a=e$
* Nếu $\forall a,b \in G$ và $a*b=b*a$ thì nhóm G được gọi là nhóm giao hoán (nhóm Abel)

[Tài liệu tham khảo](https://dunglq2000.github.io/abstract-algebra/groups/group.html)
#### **b. Vành (Ring)**
**Định nghĩa:** Cho tập hợp $R$, trên đó ta định nghĩa 2 toán tử cộng và nhân. Kí hiệu $(R,+,\times)$. khi đó $(R,+,\times)$ tạo thành vành nếu:
**$1. (R,+)$** là một nhóm giao hoán (Abel group).
**$2. (R,\times)$** có tính kết hợp với phép nhân $\forall a,b,c \in R$ thì $a \times (b \times c) = (a \times b) \times c$.
**$3.$ Tính phân phối của phép cộng và phép nhân:** $\forall a,b,c \in R$ thì $a \times (b + c) = (a \times b) + (a \times c)$.
[Tài liệu tham khảo](https://dunglq2000.github.io/abstract-algebra/rings/ring.html)
#### **c. Trường (Field)**
**Định nghĩa:** Tập hợp $F$ là một tập hợp các phần tử thỏa mãn các tính chất sau:
**$1.$ Tập hợp F với phép toán cộng $(F, +)$** tạo thành một nhóm $+$ với phần tử trung hòa là 0.
**$2.$ Tập hợp $(F, \times)$ \ {0}** tạo thành một nhóm $\times$ với phần tử trung hòa là 1.
**3. Luật phân phối giữa 2 nhóm được đảm bảo:** $\forall a,b,c \in F$ thì $a \times (b + c) = (a \times b) + (a \times c)$
**Ví dụ:** tập hợp các số thực $R$, tập hợp các số phức $Q$ là những trường vô hạn. Tuy nhiên, mật mã nói chung và hệ mã AES nói riêng sẽ làm việc trong một trường hữu hạn $GF$.
#### d. Trường hữu hạn (finite field).
Trong mật mã học, chúng ta hầu hết làm việc trong một tập hợp hữu hạn các phần tử. Đó gọi là trường hữu hạn (finite field) hay trường Galois (GF). Số lượng phần tử trong một trường được gọi là cấp của trường (order).
* **Định lý 1:** một trường với cấp $m$ chỉ tồn tại nếu $m$ là luỹ thừa một số nguyên tố $m=p^n$ với $n$ là một số nguyên dương và $p$ là số nguyên tố.
Ví dụ: Trường hữu hạn với 11 phần tử vì $11=11^1$ hoặc 81 phần tử vì $81 = 3^4$, nhưng không có trường hữu hạn 12 phần tử vì $12$ không thể biểu diễn bằng $p^n$ và $12$ không là số nguyên tố.
* **Định lý 2:** cho p là một số nguyên tố, Vành số nguyên $Z_p$ được kí hiệu là $GF(p)$ và được gọi là một trường nguyên tố, hoặc là trường Galois với số lượng phần tử là một số nguyên tố. Tất cả các phần tử khác $0$ của $GF(p)$ đều có phần tử nghịch đảo, các phép toán trong $GF(p)$ được thực hiện theo modulo $p$.
Ví dụ: ta có trường hữu hạn $GF(5) = \lbrace 0,1,2,3,4\rbrace$.
![image](https://hackmd.io/_uploads/rk36n4Xy-e.png)
Tất cả những phép toán trên điều có kết quả nằm trong $GF(5)$.
#### e. Trường mở rộng (Extension field)
* **Định lý 3:** Với $m = p^n$ và $n > 1$. Ta có trường $GF(p^n)$ là một trường mở rộng. Các phần tử trong trường mở rộng được biểu diễn dưới dạng đa thức, các phép toán trong trường mở rộng là những phép toán đa thức.

Trong hệ mã AES ta làm việc trong một trường mở rộng $GF(2^8)$ các phần tử trong trường này là những đa thức có bậc tối đa là $7$ và hệ số nằm trong trường $GF(2)$.
![image](https://hackmd.io/_uploads/HyC_BdVkWe.png)

* Đối với phép cộng hoặc trừ của đa thức trong trường mở rộng $GF(2^m)$, ta chỉ cần cộng hoặc trừ các hệ số có bậc bằng nhau cùng phép toán modulo như thông thường. **Key addition player** trong AES sử dụng phép cộng trong trường mở rộng.
![image](https://hackmd.io/_uploads/SkmnPuN1We.png)
![image](https://hackmd.io/_uploads/Bktb__Eybx.png)
* Đối với phép nhân trong trường mở rộng $GF(2^m)$, ta thực hiện phép nhân đa thức và chia lấy phần dư với một đa thức bất khả quy. Phép nhân là phép toán chính của **MixColumns** trong AES.
![image](https://hackmd.io/_uploads/BJpVytE1-x.png)
Đa thức bất khả quy là đa thức không thể phân tích thành những đa thức có bậc lớn hơn 1. Nghĩa là $P(x)$ không thể phần tích thành $P(x) = Q(x).R(x)$. Đa thức bất khả quy có thể xem tương đương như một số nguyên tố. Trong AES, đa thức bất khả quy được chọn là $m(x) = x^8 + x^4 + x^3 + x + 1$.
* Phép nghịch đảo trong trường mở rộng $GF(2^m)$ cũng tương tự như phép nghịch đảo modulo của số nguyên
![image](https://hackmd.io/_uploads/B1lIQKN1Wg.png)
Phép nghịch đảo là phép toán trong **Byte Substitution** trong AES.

### 2. Cấu trúc AES

![image](https://hackmd.io/_uploads/S1095PdyZl.png)
![image](https://hackmd.io/_uploads/rJi6AI_ybg.png)

**Giai đoạn 1:** Ta có bản rõ 128 bit sẽ được chia thành từng đoạn 8 bit và sẽ được xếp một cách tuần tự vào ma trận trạng thái (State).
![image](https://hackmd.io/_uploads/HJNPGPOyWx.png)
> D0 tương ứng với 8 bit đầu tiên, D1 là 8 bit tiếp theo cho đến DF là 8 bit cuối cùng của plaintext.

**Giai đoạn 2:** trải qua các vòng lặp mã hóa.
* **Vòng đầu tiên:** Đây là vòng khởi tạo (Initial Round) chỉ duy nhất một phép biến đổi là **Addroundkey**. Sử dụng khối ma trận State ở giai đoạn 1 để thực hiện phép XOR với khóa vòng đầu tiên $k_0$.
* **Các vòng lặp chính:** ở mỗi vòng lặp khối state đầu vào sẽ phải trả qua 4 hàm chức năng theo thứ tự.
![image](https://hackmd.io/_uploads/SyjouwuJ-x.png)
Chi tiết các hàm trong vòng lặp sẽ được giải thích ở phần sau.
* **Vòng cuối cùng:** về mặt thứ tự và thuật toán, vòng cuối cùng vẫn có cấu trúc giống với các vòng chính, nhưng sẽ bỏ qua hàm **MixColumns**.
![image](https://hackmd.io/_uploads/rkgYKDukbx.png)
> Sau vòng lặp cuối cùng sẽ có bản mã Ciphertext.
### 3. Các phép biến đổi trong mỗi vòng lặp của AES.
![image](https://hackmd.io/_uploads/H1EfowOy-e.png)
#### **Phép biến đổi 1: SubBytes (Bytes substitution)**.
Đây là phép biến đổi đầu tiên trong mỗi vòng lặp. Có mục đích thực hiện phép thay thế phi tuyến trên từng byte riêng lẻ của ma trận **State**. Mỗi byte trong ma trận `State` được ánh xạ (thay thế) thông qua một bảng tra cứu cố định gọi là `S-box` (Substitution Box).
![image](https://hackmd.io/_uploads/SJ01TOdJWg.png)
![image](https://hackmd.io/_uploads/SkNxTOuJbe.png)
![image](https://hackmd.io/_uploads/r12zp_OJWx.png)
#### **Phép biến đổi 2: Shiftrow (Row Shifting)**.
Shiftrows là một phép hoán vị (permutation) quan trọng, dịch chuyển vòng các byte trong mỗi hàng của ma trận `state`.
![image](https://hackmd.io/_uploads/ryzdCduk-g.png)
**Quy tắt dịch chuyển hàng:** 
+ Hàng 0 $(S_{0.0},...,S_{0.3})$ không dịch chuyển.
+ hàng 1 $(S_{1.0},...,S_{1.3})$ dịch sang trái 1 byte.
+ Hàng 2 $(S_{2.0},...,S_{2.3})$ dịch sang trái 2 byte.
+ Hàng 3 $(S_{3.0},...,S_{3.3})$ dịch sang trái 3 byte.

#### **Phép biến đổi 3: MixColumns (Columns mixing)**.
`MixColumns` là một phép biến đổi tuyến tính, có mục đích chính là trộn (mix) các byte trong mỗi cột của ma trận `state`.
Mỗi cột 4-byte của State được nhân với một ma trận 4x4 cố định. Điều quan trọng là tất cả các phép toán (cộng và nhân) trong ma trận này đều được thực hiện trong trường hữu hạn GF(2⁸), đảm bảo kết quả luôn là một byte.
![image](https://hackmd.io/_uploads/Bk9IMc_k-g.png)
> Nguyên tắt nhân 2 ma trận là lấy hàng ma trận này nhân với cột của ma trận kia. Đầu tiên, ta lấy cột 1 của ma trận `state`  $(S_{0.0},...,S_{3.0})$ sắp nhân với ma trận cố định như trong ảnh.
> ![image](https://hackmd.io/_uploads/rkTAXcdk-l.png)
Lưu ý: Đừng hiểu lầm các giá trị $01,02,03$ trong ma trận cố định là những số thập phân thông thường, đó là các phần tử trong trường $GF(2^8)$, mà các phần tử trong trường này là những đa thức.
![image](https://hackmd.io/_uploads/r15SLqO1We.png)

Đây là một phép biến đổi khó nhất trong AES vì liên quan nhiều đến toán học và đại số tuyến tính. Để hiểu rõ hơn, hãy tham khảo [wikipedia (Rijndael_MixColumns)](https://en.wikipedia.org/wiki/Rijndael_MixColumns) và các nguồn khác.

#### **Phép biến đổi 4: AddRoundKey (add round key)**.
![image](https://hackmd.io/_uploads/SJlgqc_kWl.png)
Đây là phép biến đổi đơn giản trong AES, chỉ đơn giản là thực hiện phép toán XOR từng cột của ma trận `state` với từng word của `round key`, word tương ứng với cột của ma trận `round key`.

### 4. Thuật toán tạo khóa vòng (Key schedule or Key expansion).
![image](https://hackmd.io/_uploads/B1lZp1KJbx.png)
Với mỗi khóa có kích thước khác nhau sẽ có số vòng vập khác nhau, và số khóa bằng số vòng lặp cộng thêm 1.
![image](https://hackmd.io/_uploads/SJhv7eY1-g.png)
Trong quá trình Sinh Khóa Vòng (Key Expansion), một chuỗi các từ (word) 32-bit được tạo ra tuần tự. Mỗi từ mới (`w[i]`) được hình thành dựa trên các từ đã có.
![image](https://hackmd.io/_uploads/SkuiQgKJbx.png)
![image](https://hackmd.io/_uploads/S18Amxtk-l.png)
* **Hàm g (`g function`):** Đây là hàm biến đổi đặc biệt gồm 3 thao tác:
    * **RotWord (Dịch vòng byte):** Dịch vòng các byte trong `W` sang trái 1 vị trí. Ví dụ: [b0, b1, b2, b3] trở thành [b1, b2, b3, b0].
    * **SubWord (thay thế S-Box):** Lấy từng byte sau khi hoàn thành `RotWord` và thay thế bằng giá trị tương ứng sau khi tra cứu S-Box. Bảng S-Box và thao tác tra cứu giống như ở phần `SubBytes`.
    * **XOR Rcon (XOR với Round constant)**: Lấy kết quả sau khi thực hiện SubWord XOR với một hằng số vòng `Rcon[i/Nr]` với Nr là số từ của biến thể AES. `Rcon` chỉ có giá trị ở byte đầu tiên, các byte còn lại điều là $0x00$.
![image](https://hackmd.io/_uploads/SyKetgt1Wl.png)

**Lưu ý:** Sau khi tạo ra các `W`, các `W` sẽ được lưu trong mảng 1 chiều, và sẽ được truy vấn khi thực hiện hàm `AddRoundKey` trong mỗi vòng lặp.
>Ngoài ra đối với `AES-256` ta có thêm một hàm là hàm `h`, hàm này chỉ thực hiện bước `SubWord`. Hiện tại, ta chỉ cần hiểu đầy là hàm tăng cường tính phòng thủ riêng cho `AES-256` và tránh tồn tại các mối quan hệ tuyến tính trong `AES-256`.
>![image](https://hackmd.io/_uploads/Sk5xjgY1bl.png)

### 5. Giải mã trong AES.
![image](https://hackmd.io/_uploads/B1pMTlYkbl.png)
Để Giải mã trong AES, ta sử dụng phép nghịch đảo các hàm và đảo ngược thứ tự sử dụng khóa (`W`).
**InvSubBytes**: Sử dụng bảng S-Box nghịch đảo của AES để tra cứu.
![image](https://hackmd.io/_uploads/BJ7el-t1bl.png)
**InvShiftRow**: Hiểu đơn giản là thay vì dịch byte ở hàng 2,3,4 sang trái ta sẽ dịch byte sang phải, quy tắc và thứ tự vẫn giữ nguyên giống hàm `ShiftRow`.
**InvMixColumns**: Đây là hàm khó nhất nhưng chỉ cần hiểu là nhân mỗi cột của ma trận trạng thái `state` với ma trận nghịch đảo của ma trận cố định.
![image](https://hackmd.io/_uploads/S1DBWZtJ-g.png)
**InvAddRoundKey**: do tính chất của phép XOR, $P \oplus K = C$ và $C \oplus K = P$. Vì vậy, mã hóa thế nào thì giải mã cũng thực hiện tương tự.

### 6. Chế độ GCM_mode (Galois Counter Mode) trong AES.
Ngoài 6 chế độ ở phần trên, AES cũng còn nhiều chế độ khác. Nhưng hiện tại ta chỉ cần tìm hiểu thêm **GCM_mode**.
![image](https://hackmd.io/_uploads/BJ-Fv-YJbe.png)
> `IV`: Vecter khởi tạo
> `counter`: bộ đếm, được tăng dần từng bước.
> `incr`: (increment) - theo tác tăng bộ đếm lên 1.
> `E_k`: Hàm mã hóa khối AES với khóa bí mật K. Mỗi counter khi đi qua AES (Eₖ) sẽ tạo ra một keystream block dùng để XOR với plaintext.
> `mult_H`: Phép nhân trong trường hữu hạn GF(2¹²⁸) với phần tử H, nơi H = Eₖ(0¹²⁸) (AES của khối toàn số 0). Đây là phần cốt lõi của hàm GHASH, dùng để tính tag xác thực.

**Nguyên lý hoạt động:** Kết hợp giữa `CTR mode` và `GHASH (Galois Hash)`. CTR dùng để mã hóa và giải mã, GHASH dùng để xác thực.
1. IV → sinh Counter 0, Counter 1, Counter 2.
2. Mỗi Counter được mã hóa bằng AES → tạo keystream.
3. Plaintext XOR keystream → Ciphertext.
4. Ciphertext và Auth Data được đưa vào GHASH → tính toán Authentication Tag.
5. Cuối cùng, Tag = E_K(Counter0) ⊕ GHASH.

**Ưu điểm:** Bảo mật mạnh mẽ, tốc độ cao, không cần padding...
**Nhược điểm:** Nhạy cảm với IV trùng lặp, Xử lý phức tạp, Tốn tài nguyên máy tính...
**Ứng dụng:** TLS/HTTPS, SSH, IPsec, Disk Encryption (Bit locker), IoT...

#### Script mã hóa và giải mã AES GCM_mode .
```python=
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

def encrypt_aes_gcm(plaintext, key):
    cipher = AES.new(key, AES.MODE_GCM)
    nonce = cipher.nonce
    ciphertext, tag = cipher.encrypt_and_digest(plaintext)

    return nonce, tag, ciphertext

def decrypt_aes_gcm(key ,nonce, tag, ciphertext):
    decipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
    plaintext = decipher.decrypt_and_verify(ciphertext, tag)
    
    return plaintext

def create_key(B):
    key = get_random_bytes(B)
    return key

def main():
    
    choice = '''Key length ?
    1. 128 bit
    2. 192 bit
    3. 256 bit'''
    while True:
        message = input("Your message: ")
        message = message.encode('utf-8')

        print(choice)
        while True:
            try:
                chosen = int(input("Your choice: "))
                if 1 <= chosen and chosen <= 3:
                    break
                else:
                    print("please! only choose 1,2 or 3 !! ")
                    continue
            except Exception as e:
                print("Please! only choose integer number !")
                continue
        
        if chosen == 1:
            key = create_key(16)
        elif chosen == 2:
            key = create_key(24)
        else:
            key = create_key(32)
        
        nonce, tag, ciphertext = encrypt_aes_gcm(message, key)
        info = f'''----------------Encryption------------------
Key: {key.hex()}
Nonce: {nonce.hex()}
Tag: {tag.hex()}
Ciphertext: {ciphertext.hex()}
-----------------------------------------'''
        print(info)

        try:
            plaintext = decrypt_aes_gcm(key, nonce, tag, ciphertext)
            print("-----------decryption-------------")
            print(f"Plaintext: {plaintext.decode('utf-8')}")
            print("----------------------------------")
        except Exception as e:
            print("Authentication failed! The data can be changed.")
        
        print("Continue ?", "1. Yes", "2. No" , sep= "\n")
        while True:
            try:
                chosen_ = int(input("Your choice: "))
                if 1 <= chosen_ and chosen_ <= 2:
                    break
                else:
                    print("please! only choose 1 or 2 !! ")
                    continue
            except Exception as e:
                print("Please! only choose integer number !")
                continue
        if chosen_ == 1:
            continue
        else:
            break

if __name__ == '__main__':
    main()
```
## IV. Reference
[2.understanding-cryptography-by-christof-paar-.pdf](https://drive.google.com/file/d/1AZt2cHfiFiTCjaCmoyZAQZxxrNTYDOgB/view?usp=sharing)
[Mật mã AES](https://drive.google.com/file/d/1w9YWFWoyw_nvh3f8kcf0p82QwFfoupyZ/view?usp=sharing)
[Mật mã DES](https://drive.google.com/file/d/1mpT_KBbn8MQPphi9XdGvSVS_JTqnZRzb/view?usp=sharing)
Kênh youtube Nesco Acedemy
[AES Rijndael Cipher explained as a Flash animation](https://youtu.be/gP4PqVGudtg?si=zz2KbcXqWb4nk9UP)
[Thuật toán mật mã DES](https://youtu.be/YAITYYw_Cds?si=YX71KywIrLXkfozg)