Thứ Bảy, 5 tháng 10, 2019

[AES] Bài 4 - Thiết kế bộ giải mã AES-128

Bài viết này phân tích một thiết kế về bộ giải mã (decrypt/decipher) cho chuẩn mã hóa dữ liệu AES-128. Nội dung ngắn gọn về lý thuyết giải mã và mã hóa các bạn xem thêm trong các bài viết trước, bài 1, bài 2bài 3.
*inv là viết tắt của "inverse" với nghĩa là "đảo"
1) Mô tả tổng quan thiết kế
Bộ giải mã AES-128, aes128_decipher_top, được thiết kế gồm 2 khối:
  • Khối mã hóa dữ liệu aes128_cipher_core_inv
  • Khối tính toán tạo khóa vòng aes128_key_expansion
Bộ mã hóa gồm các tín hiệu giao tiếp sau:
  • clk_sys: clock đồng bộ
  • rst_n: reset tích cực mức thấp
  • round_key_10[127:0]: khóa vòng thứ 10. Giá trị này được sinh ra từ khóa mã thông qua quá trình KeyExpansion.
  • cipher_text[127:0]: dữ liệu cần giải mã, bản mã
  • decipher_en: tín hiệu cho phép bộ giải mã hoạt động, tích cực 1 chu kỳ xung clock clk_sys để yêu cầu bộ giải mã hoạt động
  • decipher_ready: báo trạng thái bộ giải mã sẵn sàng hoạt động. Tín hiệu này chỉ bằng 0 khi bộ giải mã đang trong quá trình giải mã dữ liệu.
  • plain_text[127:0]: dữ liệu sau khi được giải mã, bản rõ.
Các tín hiệu giao tiếp giữa khối aes128_cipher_core_inv và aes128_key_expansion_inv:
  • rkey_en: tín hiệu cho phép khối aes128_key_expansion_inv hoạt động
  • round_num[3:0]: tín hiệu báo vòng lặp giải mã hiện tại, round_num[3:0]=0 là bước khởi tạo đầu tiên.
  • round_key_inv_out[127:0] là giá trị khóa vòng từ khối aes128_key_expansion_inv gửi cho khối aes128_cipher_core_inv. Ở lần lặp cuối cùng, round_num[3:0]=10, giá trị này chính là khóa mã gốc.
Bên cạnh đó, thiết kế sử dụng một số function được đặt trong các file sau:
  • aes128_mul_inv.sv: file chưa các function nhân
  • aes128_sbox.sv: file chứa function tra bảng S-BOX
  • aes128_sbox_inv.sv: file chứa function tra bảng S-BOX đảo
  • aes128_rcon_inv.sv: file chứa function chọn giá trị Rcon cho quá trình tính khóa vòng đảo.
Hình 1: Sơ đồ khối bộ giải mã AES-128
2) Mạch logic khối aes128_key_expansion_inv

Khối này thực hiện chức năng tính khóa vòng đảo InvKeyExpansion đã trình bày ở bài 2.
Khối này sử dụng một thanh ghi round_key_reg[127:0] để lưu lại giá trị khóa vòng sau mỗi lần tính toán.
Các bạn hãy xem từ MUX, dưới cùng của hình, lên sẽ hình dung được hoạt động của mỗi lần lặp tính khóa vòng (round key).
round_num[3:0] sẽ cho biết khóa vòng thứ mấy đang được tính, nếu nó bằng 0 thì khóa vòng số 10 round_key_10[127:0] sẽ nối đến key_in[127:0] và đưa vào mạch logic để tính ngược về khóa vòng số 9.
Từ lần tính tiếp theo trở đi, round_num[3:0] bằng 1 đến 10, giá trị được dùng để tính toán sẽ lấy từ thanh ghi round_key_reg[127:0] để tính các khóa vòng số 8, số 7, ... và khóa mã ban đầu.
Ở đây 32 bit cuối [31:0] được XOR với 32 bit kế tiếp [63:32] trước khi đi qua các bước tinh toán sau:
  • RotWord: đảo byte MSB xuống LSB
  • SubWord: ứng với function aes128_sbox()
  • AddRcon: lấy ngõ ra SubWord XOR với giá trị Rcon chọn từ function aes128_rcon_inv(). Function này chọn Rcon theo giá trị của round_num[3:0]. Thứ tự giá trị Rcon được dùng bị đảo so với quá trình mã hóa.
Kết quả sau khi AddRcon sẽ được dùng để tạo ra các bit MSB [127:96] của round_key[127:0] và lưu vào thanh ghi round_key_reg[127:0].
Hình 2: Mạch logic của khối aes128_key_expansion_inv
3) Mạch logic khối aes128_cipher_core_inv

Khối này sử dụng một bộ đếm để điều khiển quá trình giải mã. Bộ đếm sẽ bắt đầu hoạt động khi decipher_en=1. Sau đó, nó sẽ đếm lên trong suốt quá trình giải mã, ứng với rkey_en=1, khi đó decipher_ready=0. Bộ đếm bị xóa tại vòng lặp thứ 10, bước tính cuối cùng của giải mã AES-128.
Bên cạnh đó, bit decipher_ready, tín hiệu báo trạng thái giải mã sẽ bằng 0 trong suốt quá trình giải mã và tích cực 1 khi quá trình giải mã kết thúc.
Hình 3: Bộ đếm giám sát quá trình giải mã decipher_counter[3:0] và bit báo trạng thái sẵn sàng của bộ giải mã
Bộ đếm decipher_counter[3:0] và bit trạng thái cipher_ready sẽ tạo ra các tín hiệu điều khiển quá trình giải mã.
Mạch logic bộ giải mã AES-128 gồm 1 thanh ghi plainText_reg[127:0] lưu lại giá trị sau mỗi vòng lặp mã hóa, thanh ghi này đặt sau chức năng AddRoundKey.
Chúng ta bắt đầu từ tín hiệu after_addRoundkey[127:0], tín hiệu này được tạo ra từ 1 cổng XOR, đây là chức năng addRoundKey, với các ngõ vào được lấy từ:
  • Khóa vòng số 10, round_key_10[127:0], và bản mã, cipher_text[127:0] cho lần tính đầu tiên khi decipher_en=1.
  • Khóa vòng, round_key[127:0], và giá trị trung gian cho các lần tính tiếp theo, sau lần đầu tiên. Trong đó, giá trị trung gian lấy từ:
    • Ngõ vào chức năng InvMixColumns, ứng với ngõ ra chức năng InvSubByte.
after_addRoundkey[127:0] sẽ được lưu vào thanh ghi plainText_reg[127:0] sau mỗi lần tính toán. Giá trị thanh ghi này sẽ lần lượt đi qua các chức năng:
  • InvShiftRows: chuyển đổi vị trí các byte trong 1 hàng
  • InvSubBytes: tính bằng function aes128_sbox_inv()
  • InvMixColumns: tính bằng function mixcolInv()
Chi tiết về các function, các bạn có thể tìm thấy trong RTL code

Hình 4: Mạch nguyên lý bộ giải mã AES-128
4) Kết quả tổng hợp trên FPGA

RTL code đã được tổng hợp trên Quartus II (Altera-Intel).
Hình 5: Thông tin tổng hợp trên Quartus II
5) Mô phỏng và kiểm chứng kết quả

Tác giả chưa xây dựng một môi trường mô phỏng với đầy đủ các trường hợp. Các bạn có thể dùng testbench đơn giản sau đây để kiểm chứng các trường hợp mong muốn.

module tb_cipher_inv_top;
  //input
  reg clk_sys;
  reg rst_n;
  reg [127:0] cipher_text;
  reg [127:0] round_key_10;
  reg decipher_en;
  //output
  wire [127:0] plain_text;
  wire         decipher_ready;
  //
  aes128_cipher_inv_top aes128_cipher_inv_top (
  //input
  clk_sys,
  rst_n,
  cipher_text,
  round_key_10,
  decipher_en,
  //output
  plain_text,
  decipher_ready
  );
 
  initial begin    clk_sys = 0;
    rst_n = 0;
    round_key_10 = 0;
    cipher_text = 0;
    decipher_en = 0;
  end  initial begin    forever #5 clk_sys = ~clk_sys;
  end
  initial begin
    #16
    rst_n = 1;
    #10
    decipher_en = 1;
    cipher_text = 128'h69c4e0d86a7b0430d8cdb78070b4c55a;
    round_key_10 = 128'h13111d7fe3944a17f307a78b4d2b30c5;

    #10
    decipher_en = 0;
    #120
    $display ("---- plain_text: %32h - READY: %1b\n", plain_text[127:0], decipher_ready);
    #10
    decipher_en = 1;
    cipher_text = 128'hdaba0685a6b6ef1d096f7980accf3ac5;
    round_key_10 = 128'ha5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5;

    #10
    decipher_en = 0;
    #120
    $display ("---- plain_text: %32h - READY: %1b\n", plain_text[127:0], decipher_ready);
    $stop;
  end
endmodule
Trong test này, tác giả chỉ để 2 trường hợp giúp bạn nào quan tâm có thể thử nhanh. Kết quả chạy mô phỏng như sau:
# ---- plain_text: 00112233445566778899aabbccddeeff - READY: 1
# ---- plain_text: fc018ee7550032a7cafeb9db263f00fd - READY: 1
Các bạn dùng trang web sau đây để kiểm chứng kết quả.
http://testprotect.com/appendix/AEScalc

Dữ liệu có thể tải:
Source code trên GitHub

Lịch sử cập nhật:
1) 2019.10.05 - Tạo lần đầu

5 bình luận:

  1. Admin ơi! Hình như kết quả mô phỏng so với công cụ kiểm tra online hình như không giống nhau ạ

    Trả lờiXóa
  2. e. cũng thấy không giống nhau như bạn trên ạ. hay do bọn em đang hiểu sai ở đâu ạ.

    Trả lờiXóa
  3. em cũng thấy khác nhau như bạn trên ạ, hay do bọn em đang hiểu sai ở đâu ạ

    Trả lờiXóa
  4. mình kiểm tra online thì thấy đúng mà 2 bạn ơ trên .

    Trả lờiXóa