Symmetric ciphers - Mật mã đối xứng AES (phần 2)
III. Thuật toán AES - thực hiện
Trong phần này chúng ta sẽ tìm hiểu kỹ hơn về từng công việc được thực hiện trong quá trình mã hóa, giải mã của thuật toán AES. Bao gồm cách xử lý, thao tác với khối dữ liệu, secret key, từng bước thực hiện trong mỗi round mã hóa. Loại thuật toán AES xét đến là AES-128.
1. Thao tác với khối dữ liệu
AES-128 chia văn bản cần mã hóa thành các nhóm dữ liệu, mỗi nhóm dữ liệu có độ lớn bit hay byte. Mỗi nhóm này được chuyển thể thành một ma trận , thể hiện dưới dạng một khối (block). Trong đó mỗi có độ lớn byte, được sắp xếp theo thứ tự từ trên xuống dưới, từ trái qua phải.
Trong mỗi round mã hóa, các khối này được thể hiện dưới dạng ma trận trạng thái (state matrix). Ma trận trạng thái thay đổi giá trị sau mỗi round mã hóa. Kết quả cuối cùng của ma trận này cũng chính là ma trận của văn bản mã hóa. Quan sát kỹ hơn qua sơ đồ dưới đây:
Ví dụ, với block viblosecurityh2o
, khi biểu diễn lần đầu với ma trận trạng thái, chúng ta có state table (biểu diễn ở dạng hex):
Khi thực hiện trong chương trình, chúng ta thường sử dụng một list với phần tử list (mỗi phần tử list này chứa phần tử) biểu thị cho byte.
matrix = [[], [], [], []]
Để thuận tiện cho các bước tính toán ở các bước sau, chương trình thường quy ước phần tử thứ i của list matrix
tương ứng với các phần tử cột i của block. Ví dụ với block trên chúng ta có biến matrix
mang giá trị:
matrix = [['v', 'i', 'b', 'l'],
['o', 's', 'e', 'c'],
['u', 'r', 'i', 't'],
['y', 'h', '2', 'o']]
Với quy ước như trên, hàm chuyển đổi từ bytes sang block có thể dễ dàng thực hiện như sau:
def bytes2matrix1(text):
""" Converts a 16-byte array into a 4x4 matrix. """
return [list(text[i:i+4]) for i in range(0, len(text), 4)]
Đối với quá trình giải mã ngược lại, các bạn cần đưa các phần tử từ block trở về chuỗi byte. Có thể luyện tập với challenge Structure of AES. Mục đích là hoàn thiện hàm matrix2bytes()
chuyển thể ngược lại từ ma trận trở về chuỗi byte dựa vào hàm bytes2matrix()
được đề bài đưa ra:
def bytes2matrix(text):
""" Converts a 16-byte array into a 4x4 matrix. """
return [list(text[i:i+4]) for i in range(0, len(text), 4)]
def matrix2bytes(matrix):
""" Converts a 4x4 matrix into a 16-byte array. """
????
matrix = [
[99, 114, 121, 112],
[116, 111, 123, 105],
[110, 109, 97, 116],
[114, 105, 120, 125],
]
print(matrix2bytes(matrix))
2. Thao tác với khóa - AES key schedule
Như chúng ta đã biết, trong mã hóa AES dữ liệu được đưa vào các vòng (round) mã hóa liên tục, số round mã hóa phụ thuộc vào loại mật mã AES sử dụng. Secret key bit sẽ được sử dụng trong mỗi vòng mã hóa. Mỗi round cần một khóa có độ dài bit. Tuy nhiên, không phải tất cả các round đều sử dụng cùng một secret key. Vậy thì, chỉ với một secret key có độ dài bit, làm sao để có thể sử dụng cho các round mã hóa (mỗi round cũng cần một key dài bit) mà không bị trùng lặp? Lúc này chúng ta cần thực hiện một thao tác có tên gọi: Expand key - mở rộng khóa.
2.1. Round constants
Round constants định nghĩa các mảng . Trong đó mỗi mảng bao gồm phần tử, mỗi phần tử có độ lớn bit.
Giá trị được tính toán như sau:
Như vậy, chúng ta có giá trị của được liệt kê trong bảng sau:
2.2. The key schedule
Tương tự như trên, secret key được chuyển thể thành một ma trận sắp xếp từng byte theo thứ tự từ trên xuống dưới, từ trái sang phải. Ví dụ với khóa chúng ta có block:
Trước khi đến với kỹ thuật expand key, chúng ta cần tìm hiểu một số định nghĩa.
Trong ma trận trên, định nghĩa mỗi cột là một "từ" có độ dài bit (-bit word).
Hàm thực hiện "dịch byte" của mảng bit sang bên trái một vị trí. Cụ thể:
Hàm thực hiện thay thế các byte trong mảng bit thành các giá trị tương ứng định quy định trong bảng S-box (Khái niệm sẽ tìm hiểu kỹ hơn trong phần sau). Cụ thể:
Secret key sẽ được mở rộng thành một chuỗi tạo bởi word, ký hiệu là . Với lần lượt tạo bởi bốn cột của secret key gốc. Các word từ đến được tính toán dựa vào các hàm định nghĩa trên. Tổng quát, chúng ta có quy tắc tính toán các word trong expand key:
Như vậy, đối với loại mật mã AES-128, với secret key có độ dài ban đầu sẽ được mở rộng thành nhóm key mới, mỗi nhóm chứa word (tổng word như đã nêu trên). Từng nhóm key này sẽ được sử dụng trong mỗi round trong cả quá trình mã hóa và giải mã AES.
Về cách thực hiện tổng quát expand key với loại mật mã AES bất kỳ, bạn đọc có thể tham khảo thêm tại https://en.wikipedia.org/wiki/AES_key_schedule.
3. Tổng quan các round mã hóa và giải mã trong AES-128
AES | Độ lớn mỗi khối (block) | Độ lớn khóa bí mật (secret key) | Số vòng mã hóa trong mỗi khối (Rounds) |
---|---|---|---|
AES-128 | 128 bit | 128 bit | 10 |
AES-192 | 128 bit | 192 bit | 12 |
AES-256 | 128 bit | 256 bit | 14 |
Đối với AES-128, quá trình mã hóa thực hiện tổng vòng (rounds). Có loại công việc được thực hiện và tạo thành các round này là: AddRoundKey, SubBytes, ShiftRows, MixColumns. Về cụ thể các công việc thực hiện trong mỗi loại sẽ được giới thiệu kỹ hơn trong phần sau. Tổng quan quá trình mã hóa có thể quan sát qua sơ đồ sau:
(Sơ đồ tổng quát với mật mã AES, trong trường hợp này Nr = 10)
Trước khi đi vào round đầu tiên, thuật toán thực AddRoundKey đối với block, sau đó từ round tới round thực hiện nhóm công việc lần lượt theo thứ tự: SubBytes, ShiftRows, MixColumns, AddRoundKey. Riêng round cuối cùng chỉ thực hiện công việc SubBytes, ShiftRows, AddRoundKey.
Các nhóm secret key sau khi mở rộng sẽ được sử dụng trong duy nhất loại công việc AddRoundKey. Như các bạn thấy, có lần AddRoundKey, cũng là lý do vì sao khóa được mở rộng thành nhóm.
Quá trình giải mã thực hiện ngược lại lần lượt các công việc trong mã hóa, với quy ước các round gồm nhóm công việc thay đổi một chút:
Bạn đọc có thể quan sát sơ đồ tổng quát sau cho thấy các nhóm word trong expand key được sử dụng tương ứng trong quá trình mã hóa và giải mã:
Tài liệu tham khảo
All rights reserved