+3

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 128128 bit hay 1616 byte. Mỗi nhóm này được chuyển thể thành một ma trận 4×44\times4, thể hiện dưới dạng một khối (block). Trong đó mỗi PiP_i (i=0,15)(i = \overline{0,15}) có độ lớn 11 byte, được sắp xếp theo thứ tự từ trên xuống dưới, từ trái qua phải.

image.png

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:

image.png

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 11 (biểu diễn ở dạng hex):

image.png

Khi thực hiện trong chương trình, chúng ta thường sử dụng một list với 44 phần tử list (mỗi phần tử list này chứa 44 phần tử) biểu thị cho 1616 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 128128 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 128128 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 128128 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 128128 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 rconircon_i (i=1,10)(i = \overline{1,10}). Trong đó mỗi mảng rconircon_i bao gồm 44 phần tử, mỗi phần tử có độ lớn 88 bit.

rconi=[rci  0016  0016  0016]rcon_i = [rc_i\ \ 00_{16}\ \ 00_{16}\ \ 00_{16}]

Giá trị rcirc_i (i=1,10)(i = \overline{1,10}) được tính toán như sau:

rci={1neˆˊ i=12rci1neˆˊi>1 vaˋ rci1<8016(2rci1)  11B16neˆˊi>1 vaˋ rci18016rc_i= \begin{cases} 1&\text{nếu }\ i = 1\\ 2\cdot rc_{i-1}&\text{nếu }i > 1 \text{ và } rc_{i-1} < 80_{16}\\ (2\cdot rc_{i-1})\ \oplus\ 11B_{16}& \text{nếu } i > 1 \text{ và } rc_{i-1} \ge 80_{16} \end{cases}

Như vậy, chúng ta có 1010 giá trị của rcirc_i được liệt kê trong bảng sau:

ii 11 22 33 44 55 66 77 88 99 1010
rcirc_i 011601_{16} 021602_{16} 041604_{16} 081608_{16} 101610_{16} 201620_{16} 401640_{16} 801680_{16} 1B161B_{16} 361636_{16}

2.2. The key schedule

Tương tự như trên, secret key được chuyển thể thành một ma trận 4×44\times 4 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 K0K1K2...K15K_0K_1K_2...K_{15} chúng ta có block:

image.png

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 4×44\times 4 trên, định nghĩa mỗi cột là một "từ" có độ dài 3232 bit (3232-bit word).

Hàm RotWord()RotWord() thực hiện "dịch byte" của mảng 3232 bit sang bên trái một vị trí. Cụ thể:

RotWord([b0 b1 b2 b3])=[b1 b2 b3 b0]RotWord([b_0\ b_1\ b_2\ b_3]) = [b_1\ b_2\ b_3\ b_0]

Hàm SubWord()SubWord() thực hiện thay thế các byte trong mảng 3232 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ể:

SubWord([b0 b1 b2 b3])=[S(b0) S(b1) S(b2) S(b3)]SubWord([b_0\ b_1\ b_2\ b_3]) = [S(b_0)\ S(b_1)\ S(b_2)\ S(b_3)]

Secret key sẽ được mở rộng thành một chuỗi tạo bởi 4444 word, ký hiệu là W[0],W[1],W[2],...,W[43]W[0], W[1], W[2], ..., W[43]. Với W[0],W[1],W[2],W[3]W[0], W[1], W[2], W[3] lần lượt tạo bởi bốn cột của secret key gốc. Các word từ W[4]W[4] đến W[43]W[43] đượ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:

Wi={Kiif i<NWiNSubWord(RotWord(Wi1))rconi/NneˆˊiN vaˋ i0(modN)WiNSubWord(Wi1)neˆˊiNN>6, vaˋ i4(modN)WiNWi1Caˊc trường hợp coˋn lạiW_i = \begin{cases} K_i & \text{if } i < N \\ W_{i-N} \oplus \operatorname{SubWord}(\operatorname{RotWord}(W_{i-1})) \oplus rcon_{i/N} & \text {nếu } i \ge N \text{ và } i \equiv 0 \pmod{N} \\ W_{i-N} \oplus \operatorname{SubWord}(W_{i-1}) & \text{nếu } i \ge N \text{, } N > 6 \text{, và } i \equiv 4 \pmod{N} \\ W_{i-N} \oplus W_{i-1} & \text{Các trường hợp còn lại} \\ \end{cases}

Như vậy, đối với loại mật mã AES-128, với secret key có độ dài 128128 ban đầu sẽ được mở rộng thành 1111 nhóm key mới, mỗi nhóm chứa 44 word (tổng 4444 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 1010 vòng (rounds). Có 44 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:

image.png (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 11 tới round 99 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 33 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ó 1111 lần AddRoundKey, cũng là lý do vì sao khóa được mở rộng thành 1111 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:

image.png

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

image.png

Tài liệu tham khảo


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí