Verilog

[Verilog] AES 128

Below_zero 2025. 3. 8. 04:16

Verilog 프로젝트로, 128비트 입력 및 키가 주어졌을 때의 AES 암호화 및 복호화 알고리즘을 구현했다.

 

https://www.researchgate.net/publication/317615794_Advanced_Encryption_Standard_AES_Algorithm_to_Encrypt_and_Decrypt_Data

 

AES 암호화란 간단하게 설명하자면 입력으로 암호화할 텍스트와 키를 받고, Subbyte - Shiftrows - Mixcolumns - Addroundkey라는 암호화 스테이지를 10번 반복해서 암호화하는 알고리즘이다.

 

그래서 각각의 암호화 스테이지를 함수(task 및 function)으로 미리 구현하여 사용하였다.

첨부한 링크에서 발췌한 AES 구조

 

아래 코드는 Verilog로 작성하기 전, 먼저 C언어로 작성해본 AES 128 암호화 복호화 코드이다.

 

#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

static const uint8_t sbox[256] = {
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};


static const uint8_t inv_sbox[256] = {
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};

// Rcon 배열
static const uint32_t Rcon[11] = {
    0x00000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000,
    0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1b000000, 0x36000000
};

// RotWord 함수
static uint32_t RotWord(uint32_t word) {
    return (word << 8) | (word >> 24);
}

// SubWord 함수
static uint32_t SubWord(uint32_t word) {
    return (sbox[word >> 24] << 24) |
        (sbox[(word >> 16) & 0xFF] << 16) |
        (sbox[(word >> 8) & 0xFF] << 8) |
        (sbox[word & 0xFF]);
}

// [0]-w[1]-w[2]-w[3] 순서로 붙여서 key 만드는 함수
void combine_words_to_key(const uint32_t* w, uint8_t* next_key) {
    for (int i = 0; i < 4; i++) {
        // w[i]는 총 32비트
        // 즉 w[0]은 24비트만큼 right shift로 상위 8비트만 남기고
        // w[1]은 16비트만큼 right shift하면 상위 16비트가 남는데, & 0xFF로 마스킹을 통해 상위 8비트, 즉 중간 상위 8비트만 남김
        // w[2], w[3]도 같은 원리

        next_key[i * 4] = (w[i] >> 24) & 0xFF;     // 상위 8비트
        next_key[i * 4 + 1] = (w[i] >> 16) & 0xFF; // 중간 상위 8비트
        next_key[i * 4 + 2] = (w[i] >> 8) & 0xFF;  // 중간 하위 8비트
        next_key[i * 4 + 3] = w[i] & 0xFF;         // 하위 8비트
    }
}

// 이후 라운드키 구하기
void key_expansion(const uint8_t* current_key, uint8_t* next_key, int round) {
    uint32_t w[4];
    uint32_t temp;

    // Endian 고려해서 자리변경 (w0 - w1 - w2 - w3 순서)
    w[0] = (current_key[0] << 24) | (current_key[1] << 16) | (current_key[2] << 8) | current_key[3];
    w[1] = (current_key[4] << 24) | (current_key[5] << 16) | (current_key[6] << 8) | current_key[7];
    w[2] = (current_key[8] << 24) | (current_key[9] << 16) | (current_key[10] << 8) | current_key[11];
    w[3] = (current_key[12] << 24) | (current_key[13] << 16) | (current_key[14] << 8) | current_key[15];

    temp = RotWord(w[3]);
    temp = SubWord(temp) ^ Rcon[round];

    w[0] ^= temp;
    w[1] ^= w[0];
    w[2] ^= w[1];
    w[3] ^= w[2];
    
    // w[0]-w[1]-w[2]-w[3] 순서로 붙인 결과를 next_key에 전달
    combine_words_to_key(w, next_key);
}

// 이전 라운드키 구하기
void inv_key_expansion(const uint8_t* current_key, uint8_t* prev_key, int round) {
    uint32_t w[4];
    uint32_t temp;

    // endian 고려해서 자리변경 (w0 - w1 - w2 - w3 순서)
    w[0] = (current_key[0] << 24) | (current_key[1] << 16) | (current_key[2] << 8) | current_key[3];
    w[1] = (current_key[4] << 24) | (current_key[5] << 16) | (current_key[6] << 8) | current_key[7];
    w[2] = (current_key[8] << 24) | (current_key[9] << 16) | (current_key[10] << 8) | current_key[11];
    w[3] = (current_key[12] << 24) | (current_key[13] << 16) | (current_key[14] << 8) | current_key[15];


    w[3] = w[3] ^ w[2];
    w[2] = w[2] ^ w[1];
    w[1] = w[1] ^ w[0];
    

    temp = w[3];
    temp = RotWord(temp);
    temp = SubWord(temp);
    temp = temp ^ Rcon[round + 1];
    temp = temp ^ w[0];

    w[0] = temp;

    // w[0]-w[1]-w[2]-w[3] 순서로 붙인 결과를 prev_key에 전달
    combine_words_to_key(w, prev_key);
}



// AddRoundKey 함수
void add_round_key(uint8_t* state, const uint8_t* round_key) {
    for (int i = 0; i < 16; i++) {
        state[i] ^= round_key[i];
    }
}

// SubBytes 함수
void sub_bytes(uint8_t* state) {
    for (int i = 0; i < 16; i++) {
        state[i] = sbox[state[i]];
    }
}

// ShiftRows 함수
void shift_rows(uint8_t* state) {
    uint8_t temp[16];

    temp[0] = state[0];
    temp[1] = state[5];
    temp[2] = state[10];
    temp[3] = state[15];

    temp[4] = state[4];
    temp[5] = state[9];
    temp[6] = state[14];
    temp[7] = state[3];

    temp[8] = state[8];
    temp[9] = state[13];
    temp[10] = state[2];
    temp[11] = state[7];

    temp[12] = state[12];
    temp[13] = state[1];
    temp[14] = state[6];
    temp[15] = state[11];

    memcpy(state, temp, 16);
}




// mix_column 연산에 사용하는 mul 함수들
static uint8_t mul2(uint8_t x) {
    return (x << 1) ^ ((x & 0x80) ? 0x1b : 0x00);
}

static uint8_t mul3(uint8_t x) {
    return mul2(x) ^ x;
}

static uint8_t mul9(uint8_t x) {
    return mul2(mul2(mul2(x))) ^ x;
}

static uint8_t mul11(uint8_t x) {
    return mul2(mul2(mul2(x)) ^ x) ^ x;
}

static uint8_t mul13(uint8_t x) {
    return mul2(mul2(mul2(x) ^ x)) ^ x;
}

static uint8_t mul14(uint8_t x) {
    return mul2(mul2(mul2(x) ^ x) ^ x);
}

void mix_columns(uint8_t* state) {
    uint8_t temp[16];

    for (int i = 0; i < 4; i++) {
        uint8_t s0 = state[i * 4];
        uint8_t s1 = state[i * 4 + 1];
        uint8_t s2 = state[i * 4 + 2];
        uint8_t s3 = state[i * 4 + 3];

        temp[i * 4] = mul2(s0) ^ mul3(s1) ^ s2 ^ s3;
        temp[i * 4 + 1] = s0 ^ mul2(s1) ^ mul3(s2) ^ s3;
        temp[i * 4 + 2] = s0 ^ s1 ^ mul2(s2) ^ mul3(s3);
        temp[i * 4 + 3] = mul3(s0) ^ s1 ^ s2 ^ mul2(s3);
    }

    memcpy(state, temp, 16);
}

// sub_byte의 역 버전
void inv_sub_bytes(uint8_t* state) {
    for (int i = 0; i < 16; i++) {
        state[i] = inv_sbox[state[i]];
    }
}

// shift_rows의 역 버전
void inv_shift_rows(uint8_t* state) {
    uint8_t temp[16];

    temp[0] = state[0];
    temp[1] = state[13];
    temp[2] = state[10];
    temp[3] = state[7];

    temp[4] = state[4];
    temp[5] = state[1];
    temp[6] = state[14];
    temp[7] = state[11];

    temp[8] = state[8];
    temp[9] = state[5];
    temp[10] = state[2];
    temp[11] = state[15];

    temp[12] = state[12];
    temp[13] = state[9];
    temp[14] = state[6];
    temp[15] = state[3];

    memcpy(state, temp, 16);
}

// mix_columns의 역 버전
void inv_mix_columns(uint8_t* state) {
    uint8_t temp[16];

    for (int i = 0; i < 4; i++) {
        uint8_t s0 = state[i * 4];
        uint8_t s1 = state[i * 4 + 1];
        uint8_t s2 = state[i * 4 + 2];
        uint8_t s3 = state[i * 4 + 3];

        temp[i * 4] = mul14(s0) ^ mul11(s1) ^ mul13(s2) ^ mul9(s3);
        temp[i * 4 + 1] = mul9(s0) ^ mul14(s1) ^ mul11(s2) ^ mul13(s3);
        temp[i * 4 + 2] = mul13(s0) ^ mul9(s1) ^ mul14(s2) ^ mul11(s3);
        temp[i * 4 + 3] = mul11(s0) ^ mul13(s1) ^ mul9(s2) ^ mul14(s3);
    }

    memcpy(state, temp, 16);
}

// 암호화
void aes_encrypt(uint8_t* state, const uint8_t* key) {
    uint8_t round_key[16];
    int round = 0;

    memcpy(round_key, key, 16);


    // 초기 라운드
    add_round_key(state, round_key);
    

    // 라운드 9까지
    for (round = 1; round < 10; round++) {
        key_expansion(round_key, round_key, round);
        sub_bytes(state);
        shift_rows(state);
        mix_columns(state);
        add_round_key(state, round_key);
    }

    // 마지막 라운드는 mix_columns을 수행하지않음
    key_expansion(round_key, round_key, round);
    sub_bytes(state);
    shift_rows(state);
    add_round_key(state, round_key);
}

// 복호화
void aes_decrypt(uint8_t* state, const uint8_t* key) {
    uint8_t round_key[16];
    int round = 10;

    memcpy(round_key, key, 16);

    // 라운드 10 키 구하기
    for (int i = 1; i <= 10; i++) {
        key_expansion(round_key, round_key, i);
    }
    
    // 초기 라운드
    add_round_key(state, round_key);
    
    // 라운드 9까지
    for (round = 9; round > 0; round--) {
        inv_shift_rows(state);
        inv_sub_bytes(state);
        inv_key_expansion(round_key, round_key, round); // 이전 라운드 키로 하나씩 변환
        add_round_key(state, round_key);
        inv_mix_columns(state);
    }

    // 마지막 라운드는 inv_mix_columns을 수행하지않음
    inv_shift_rows(state);
    inv_sub_bytes(state);
    inv_key_expansion(round_key, round_key, 0);
    add_round_key(state, round_key);
}

void print_hex(const char* label, const uint8_t* data, int length) {
    printf("%15s: ", label);
    for (int i = 0; i < length; i++) {
        printf("%02x ", data[i]);
    }
    printf("\n");
}

int main() {
    // 128비트 키와 입력 데이터 정의
    uint8_t key[16] = {
        0x2b, 0x7e, 0x15, 0x16,
        0x28, 0xae, 0xd2, 0xa6,
        0xab, 0xf7, 0x15, 0x88,
        0x09, 0xcf, 0x4f, 0x3c
    };

    uint8_t plaintext[16] = {
        0x32, 0x43, 0xf6, 0xa8,
        0x88, 0x5a, 0x30, 0x8d,
        0x31, 0x31, 0x98, 0xa2,
        0xe0, 0x37, 0x07, 0x34
    };

    uint8_t encrypted[16];
    uint8_t decrypted[16];

    // 암호화
    memcpy(encrypted, plaintext, 16);
    print_hex("original text", plaintext, 16);
    print_hex("original key", key, 16);

    aes_encrypt(encrypted, key);
    print_hex("Encrypted", encrypted, 16);


    // 복호화
    memcpy(decrypted, encrypted, 16);
    aes_decrypt(decrypted, key);
    print_hex("Decrypted", decrypted, 16);

    return 0;
}

 

 

original text를 암호화하여 출력하고, 같은 키로 복호화하여 다시 original text로 변환하여 출력한다.

original text와 decrypt된 text가 동일.

 

 

위 C언어 코드를 기반으로, Verilog로 변환하는 느낌으로 작성했다. 

 

먼저 아래는 디자인 소스 코드이다.

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date: 2024/11/09 19:37:26
    // Design Name: 
    // Module Name: AES
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //////////////////////////////////////////////////////////////////////////////////
    
module AES (
    input CLK,                     // 클럭
    input nRST,                    // 리셋
    input ENCDEC,                  // 암호화/복호화 제어 (0 = 암호화, 1 = 복호화)
    input START,                   // 시작 신호
    input [127:0] KEY,             // 암호화/복호화 키
    input [127:0] TEXTIN,          // 입력문
    output reg DONE,               // 완료 신호
    output reg [127:0] TEXTOUT     // 출력문
);

    reg [127:0] state; // 암호화 및 복호화 도중의 평문
    reg [127:0] round_key; // AddRoundkey에 사용할 라운드키
    integer round; // 라운드
    reg running; // 암호화 및 복호화가 실행중인 상태 표시


    //subbyte에 사용하는 sbox
    function [7:0] sbox(input [7:0] word);
        begin
            case (word)
                8'h00: sbox = 8'h63; 8'h01: sbox = 8'h7c; 8'h02: sbox = 8'h77; 8'h03: sbox = 8'h7b;
                8'h04: sbox = 8'hf2; 8'h05: sbox = 8'h6b; 8'h06: sbox = 8'h6f; 8'h07: sbox = 8'hc5;
                8'h08: sbox = 8'h30; 8'h09: sbox = 8'h01; 8'h0a: sbox = 8'h67; 8'h0b: sbox = 8'h2b;
                8'h0c: sbox = 8'hfe; 8'h0d: sbox = 8'hd7; 8'h0e: sbox = 8'hab; 8'h0f: sbox = 8'h76;
                8'h10: sbox = 8'hca; 8'h11: sbox = 8'h82; 8'h12: sbox = 8'hc9; 8'h13: sbox = 8'h7d;
                8'h14: sbox = 8'hfa; 8'h15: sbox = 8'h59; 8'h16: sbox = 8'h47; 8'h17: sbox = 8'hf0;
                8'h18: sbox = 8'had; 8'h19: sbox = 8'hd4; 8'h1a: sbox = 8'ha2; 8'h1b: sbox = 8'haf;
                8'h1c: sbox = 8'h9c; 8'h1d: sbox = 8'ha4; 8'h1e: sbox = 8'h72; 8'h1f: sbox = 8'hc0;
                8'h20: sbox = 8'hb7; 8'h21: sbox = 8'hfd; 8'h22: sbox = 8'h93; 8'h23: sbox = 8'h26;
                8'h24: sbox = 8'h36; 8'h25: sbox = 8'h3f; 8'h26: sbox = 8'hf7; 8'h27: sbox = 8'hcc;
                8'h28: sbox = 8'h34; 8'h29: sbox = 8'ha5; 8'h2a: sbox = 8'he5; 8'h2b: sbox = 8'hf1;
                8'h2c: sbox = 8'h71; 8'h2d: sbox = 8'hd8; 8'h2e: sbox = 8'h31; 8'h2f: sbox = 8'h15;
                8'h30: sbox = 8'h04; 8'h31: sbox = 8'hc7; 8'h32: sbox = 8'h23; 8'h33: sbox = 8'hc3;
                8'h34: sbox = 8'h18; 8'h35: sbox = 8'h96; 8'h36: sbox = 8'h05; 8'h37: sbox = 8'h9a;
                8'h38: sbox = 8'h07; 8'h39: sbox = 8'h12; 8'h3a: sbox = 8'h80; 8'h3b: sbox = 8'he2;
                8'h3c: sbox = 8'heb; 8'h3d: sbox = 8'h27; 8'h3e: sbox = 8'hb2; 8'h3f: sbox = 8'h75;
                8'h40: sbox = 8'h09; 8'h41: sbox = 8'h83; 8'h42: sbox = 8'h2c; 8'h43: sbox = 8'h1a;
                8'h44: sbox = 8'h1b; 8'h45: sbox = 8'h6e; 8'h46: sbox = 8'h5a; 8'h47: sbox = 8'ha0;
                8'h48: sbox = 8'h52; 8'h49: sbox = 8'h3b; 8'h4a: sbox = 8'hd6; 8'h4b: sbox = 8'hb3;
                8'h4c: sbox = 8'h29; 8'h4d: sbox = 8'he3; 8'h4e: sbox = 8'h2f; 8'h4f: sbox = 8'h84;
                8'h50: sbox = 8'h53; 8'h51: sbox = 8'hd1; 8'h52: sbox = 8'h00; 8'h53: sbox = 8'hed;
                8'h54: sbox = 8'h20; 8'h55: sbox = 8'hfc; 8'h56: sbox = 8'hb1; 8'h57: sbox = 8'h5b;
                8'h58: sbox = 8'h6a; 8'h59: sbox = 8'hcb; 8'h5a: sbox = 8'hbe; 8'h5b: sbox = 8'h39;
                8'h5c: sbox = 8'h4a; 8'h5d: sbox = 8'h4c; 8'h5e: sbox = 8'h58; 8'h5f: sbox = 8'hcf;
                8'h60: sbox = 8'hd0; 8'h61: sbox = 8'hef; 8'h62: sbox = 8'haa; 8'h63: sbox = 8'hfb;
                8'h64: sbox = 8'h43; 8'h65: sbox = 8'h4d; 8'h66: sbox = 8'h33; 8'h67: sbox = 8'h85;
                8'h68: sbox = 8'h45; 8'h69: sbox = 8'hf9; 8'h6a: sbox = 8'h02; 8'h6b: sbox = 8'h7f;
                8'h6c: sbox = 8'h50; 8'h6d: sbox = 8'h3c; 8'h6e: sbox = 8'h9f; 8'h6f: sbox = 8'ha8;
                8'h70: sbox = 8'h51; 8'h71: sbox = 8'ha3; 8'h72: sbox = 8'h40; 8'h73: sbox = 8'h8f;
                8'h74: sbox = 8'h92; 8'h75: sbox = 8'h9d; 8'h76: sbox = 8'h38; 8'h77: sbox = 8'hf5;
                8'h78: sbox = 8'hbc; 8'h79: sbox = 8'hb6; 8'h7a: sbox = 8'hda; 8'h7b: sbox = 8'h21;
                8'h7c: sbox = 8'h10; 8'h7d: sbox = 8'hff; 8'h7e: sbox = 8'hf3; 8'h7f: sbox = 8'hd2;
                8'h80: sbox = 8'hcd; 8'h81: sbox = 8'h0c; 8'h82: sbox = 8'h13; 8'h83: sbox = 8'hec;
                8'h84: sbox = 8'h5f; 8'h85: sbox = 8'h97; 8'h86: sbox = 8'h44; 8'h87: sbox = 8'h17;
                8'h88: sbox = 8'hc4; 8'h89: sbox = 8'ha7; 8'h8a: sbox = 8'h7e; 8'h8b: sbox = 8'h3d;
                8'h8c: sbox = 8'h64; 8'h8d: sbox = 8'h5d; 8'h8e: sbox = 8'h19; 8'h8f: sbox = 8'h73;
                8'h90: sbox = 8'h60; 8'h91: sbox = 8'h81; 8'h92: sbox = 8'h4f; 8'h93: sbox = 8'hdc;
                8'h94: sbox = 8'h22; 8'h95: sbox = 8'h2a; 8'h96: sbox = 8'h90; 8'h97: sbox = 8'h88;
                8'h98: sbox = 8'h46; 8'h99: sbox = 8'hee; 8'h9a: sbox = 8'hb8; 8'h9b: sbox = 8'h14;
                8'h9c: sbox = 8'hde; 8'h9d: sbox = 8'h5e; 8'h9e: sbox = 8'h0b; 8'h9f: sbox = 8'hdb;
                8'ha0: sbox = 8'he0; 8'ha1: sbox = 8'h32; 8'ha2: sbox = 8'h3a; 8'ha3: sbox = 8'h0a;
                8'ha4: sbox = 8'h49; 8'ha5: sbox = 8'h06; 8'ha6: sbox = 8'h24; 8'ha7: sbox = 8'h5c;
                8'ha8: sbox = 8'hc2; 8'ha9: sbox = 8'hd3; 8'haa: sbox = 8'hac; 8'hab: sbox = 8'h62;
                8'hac: sbox = 8'h91; 8'had: sbox = 8'h95; 8'hae: sbox = 8'he4; 8'haf: sbox = 8'h79;
                8'hb0: sbox = 8'he7; 8'hb1: sbox = 8'hc8; 8'hb2: sbox = 8'h37; 8'hb3: sbox = 8'h6d;
                8'hb4: sbox = 8'h8d; 8'hb5: sbox = 8'hd5; 8'hb6: sbox = 8'h4e; 8'hb7: sbox = 8'ha9;
                8'hb8: sbox = 8'h6c; 8'hb9: sbox = 8'h56; 8'hba: sbox = 8'hf4; 8'hbb: sbox = 8'hea;
                8'hbc: sbox = 8'h65; 8'hbd: sbox = 8'h7a; 8'hbe: sbox = 8'hae; 8'hbf: sbox = 8'h08;
                8'hc0: sbox = 8'hba; 8'hc1: sbox = 8'h78; 8'hc2: sbox = 8'h25; 8'hc3: sbox = 8'h2e;
                8'hc4: sbox = 8'h1c; 8'hc5: sbox = 8'ha6; 8'hc6: sbox = 8'hb4; 8'hc7: sbox = 8'hc6;
                8'hc8: sbox = 8'he8; 8'hc9: sbox = 8'hdd; 8'hca: sbox = 8'h74; 8'hcb: sbox = 8'h1f;
                8'hcc: sbox = 8'h4b; 8'hcd: sbox = 8'hbd; 8'hce: sbox = 8'h8b; 8'hcf: sbox = 8'h8a;
                8'hd0: sbox = 8'h70; 8'hd1: sbox = 8'h3e; 8'hd2: sbox = 8'hb5; 8'hd3: sbox = 8'h66;
                8'hd4: sbox = 8'h48; 8'hd5: sbox = 8'h03; 8'hd6: sbox = 8'hf6; 8'hd7: sbox = 8'h0e;
                8'hd8: sbox = 8'h61; 8'hd9: sbox = 8'h35; 8'hda: sbox = 8'h57; 8'hdb: sbox = 8'hb9;
                8'hdc: sbox = 8'h86; 8'hdd: sbox = 8'hc1; 8'hde: sbox = 8'h1d; 8'hdf: sbox = 8'h9e;
                8'he0: sbox = 8'he1; 8'he1: sbox = 8'hf8; 8'he2: sbox = 8'h98; 8'he3: sbox = 8'h11;
                8'he4: sbox = 8'h69; 8'he5: sbox = 8'hd9; 8'he6: sbox = 8'h8e; 8'he7: sbox = 8'h94;
                8'he8: sbox = 8'h9b; 8'he9: sbox = 8'h1e; 8'hea: sbox = 8'h87; 8'heb: sbox = 8'he9;
                8'hec: sbox = 8'hce; 8'hed: sbox = 8'h55; 8'hee: sbox = 8'h28; 8'hef: sbox = 8'hdf;
                8'hf0: sbox = 8'h8c; 8'hf1: sbox = 8'ha1; 8'hf2: sbox = 8'h89; 8'hf3: sbox = 8'h0d;
                8'hf4: sbox = 8'hbf; 8'hf5: sbox = 8'he6; 8'hf6: sbox = 8'h42; 8'hf7: sbox = 8'h68;
                8'hf8: sbox = 8'h41; 8'hf9: sbox = 8'h99; 8'hfa: sbox = 8'h2d; 8'hfb: sbox = 8'h0f;
                8'hfc: sbox = 8'hb0; 8'hfd: sbox = 8'h54; 8'hfe: sbox = 8'hbb; 8'hff: sbox = 8'h16;
            endcase
        end
    endfunction
    
    // AddRoundKey
    task add_round_key(input [127:0] key);
        integer i;
        begin
            for (i = 0; i < 128; i = i + 8) begin
                state[i +: 8] = state[i +: 8] ^ key[i +: 8];
            end
        end
    endtask

    // SubBytes
    task sub_bytes;
        integer i;
        begin
            for (i = 0; i < 128; i = i + 8) begin
                state[i +: 8] = sbox(state[i +: 8]);
            end
        end
    endtask

    // ShiftRows
    task shift_rows;
        reg [127:0] temp;
        begin
            temp[127:120] = state[127:120];
            temp[119:112] = state[87:80];
            temp[111:104] = state[47:40];
            temp[103:96] = state[7:0];
            temp[95:88] = state[95:88];
            temp[87:80] = state[55:48];
            temp[79:72] = state[15:8];
            temp[71:64] = state[103:96];
            temp[63:56] = state[63:56];
            temp[55:48] = state[23:16];
            temp[47:40] = state[111:104];
            temp[39:32] = state[71:64];
            temp[31:24] = state[31:24];
            temp[23:16] = state[119:112];
            temp[15:8] = state[79:72];
            temp[7:0] = state[39:32];
            state = temp;
        end
    endtask
    
    // MixColumn을 위한 function mul2, mul3
    function [7:0] mul2(input [7:0] a);
        begin
            mul2 = (a[7] == 1'b1) ? (a << 1) ^ 8'h1b : (a << 1);
        end
    endfunction

    function [7:0] mul3(input [7:0] a);
        begin
            mul3 = mul2(a) ^ a;
        end
    endfunction
   
    // MixColumns, 각 Galois Field에서 정의된 다항식 곱셈을 사용하여 데이터를 선형 변환
    task mix_columns;
        integer i;
        reg [7:0] s0, s1, s2, s3;       //column의 각 바이트를 저장
        reg [7:0] out0, out1, out2, out3; // output
        begin
            for (i = 0; i < 4; i = i + 1) begin
                // 각 열의 4바이트 추출
                s0 = state[127 - 32*i -: 8];
                s1 = state[119 - 32*i -: 8];
                s2 = state[111 - 32*i -: 8];
                s3 = state[103 - 32*i -: 8];
    
                // Galois Field에서 다항식 곱셈과 XOR 연산 수행
                // 결과를 각 바이트에 저장
                out0 = mul2(s0) ^ mul3(s1) ^ s2 ^ s3;
                out1 = s0 ^ mul2(s1) ^ mul3(s2) ^ s3;
                out2 = s0 ^ s1 ^ mul2(s2) ^ mul3(s3);
                out3 = mul3(s0) ^ s1 ^ s2 ^ mul2(s3);
    
                // 변환된 바이트를 state에 저장
                state[127 - 32*i -: 8] = out0;
                state[119 - 32*i -: 8] = out1;
                state[111 - 32*i -: 8] = out2;
                state[103 - 32*i -: 8] = out3;
            end
        end
    endtask
    
    
    // keyExpansion에 사용(1) - Rcon 배열
    reg [31:0] Rcon [1:10];
    initial begin
        Rcon[1] = 32'h01000000;
        Rcon[2] = 32'h02000000;
        Rcon[3] = 32'h04000000;
        Rcon[4] = 32'h08000000;
        Rcon[5] = 32'h10000000;
        Rcon[6] = 32'h20000000;
        Rcon[7] = 32'h40000000;
        Rcon[8] = 32'h80000000;
        Rcon[9] = 32'h1b000000;
        Rcon[10] = 32'h36000000;
    end

    // KeyExpansion에 사용(2) - Subword
    function [31:0] SubWord(input [31:0] word);
        begin
            SubWord[31:24] = sbox(word[31:24]);
            SubWord[23:16] = sbox(word[23:16]);
            SubWord[15:8]  = sbox(word[15:8]);
            SubWord[7:0]   = sbox(word[7:0]);
        end
    endfunction

    // KeyExpansion에 사용(3) - Rotword
    function [31:0] RotWord(input [31:0] word);
        begin
            RotWord = {word[23:0], word[31:24]}; // 좌측 순환 이동
        end
    endfunction

    // 현재 키(current_key)를 바탕으로 다음 라운드 키(next_key)를 구하는 key_expansion 
    task key_expansion(input [127:0] current_key, output [127:0] next_key);
        reg [31:0] temp;
        reg [31:0] w [0:3];
        integer i;

        begin
            // 현재 키를 32비트 단위로 나눠 w0, w1, w2, w3에 저장
            w[0] = current_key[127:96];
            w[1] = current_key[95:64];
            w[2] = current_key[63:32];
            w[3] = current_key[31:0];
            // 마지막 워드에 대해 변환 수행
            temp = RotWord(w[3]);
            temp = SubWord(temp);
            
            temp = temp ^ Rcon[round];

            // 새로운 라운드 키 계산
            w[0] = w[0] ^ temp;
            w[1] = w[1] ^ w[0];
            w[2] = w[2] ^ w[1];
            w[3] = w[3] ^ w[2];

            // 다음 라운드 키를 생성하여 출력
            next_key = {w[0], w[1], w[2], w[3]};
        end
    endtask
    
    
    
// 복호화용 함수들

    // inv_subbyte에 사용하는 inv_sbox
    function [7:0] inv_sbox(input [7:0] word);
        begin
            case (word)
                8'h00: inv_sbox = 8'h52; 8'h01: inv_sbox = 8'h09; 8'h02: inv_sbox = 8'h6a; 8'h03: inv_sbox = 8'hd5;
                8'h04: inv_sbox = 8'h30; 8'h05: inv_sbox = 8'h36; 8'h06: inv_sbox = 8'ha5; 8'h07: inv_sbox = 8'h38;
                8'h08: inv_sbox = 8'hbf; 8'h09: inv_sbox = 8'h40; 8'h0a: inv_sbox = 8'ha3; 8'h0b: inv_sbox = 8'h9e;
                8'h0c: inv_sbox = 8'h81; 8'h0d: inv_sbox = 8'hf3; 8'h0e: inv_sbox = 8'hd7; 8'h0f: inv_sbox = 8'hfb;
                8'h10: inv_sbox = 8'h7c; 8'h11: inv_sbox = 8'he3; 8'h12: inv_sbox = 8'h39; 8'h13: inv_sbox = 8'h82;
                8'h14: inv_sbox = 8'h9b; 8'h15: inv_sbox = 8'h2f; 8'h16: inv_sbox = 8'hff; 8'h17: inv_sbox = 8'h87;
                8'h18: inv_sbox = 8'h34; 8'h19: inv_sbox = 8'h8e; 8'h1a: inv_sbox = 8'h43; 8'h1b: inv_sbox = 8'h44;
                8'h1c: inv_sbox = 8'hc4; 8'h1d: inv_sbox = 8'hde; 8'h1e: inv_sbox = 8'he9; 8'h1f: inv_sbox = 8'hcb;
                8'h20: inv_sbox = 8'h54; 8'h21: inv_sbox = 8'h7b; 8'h22: inv_sbox = 8'h94; 8'h23: inv_sbox = 8'h32;
                8'h24: inv_sbox = 8'ha6; 8'h25: inv_sbox = 8'hc2; 8'h26: inv_sbox = 8'h23; 8'h27: inv_sbox = 8'h3d;
                8'h28: inv_sbox = 8'hee; 8'h29: inv_sbox = 8'h4c; 8'h2a: inv_sbox = 8'h95; 8'h2b: inv_sbox = 8'h0b;
                8'h2c: inv_sbox = 8'h42; 8'h2d: inv_sbox = 8'hfa; 8'h2e: inv_sbox = 8'hc3; 8'h2f: inv_sbox = 8'h4e;
                8'h30: inv_sbox = 8'h08; 8'h31: inv_sbox = 8'h2e; 8'h32: inv_sbox = 8'ha1; 8'h33: inv_sbox = 8'h66;
                8'h34: inv_sbox = 8'h28; 8'h35: inv_sbox = 8'hd9; 8'h36: inv_sbox = 8'h24; 8'h37: inv_sbox = 8'hb2;
                8'h38: inv_sbox = 8'h76; 8'h39: inv_sbox = 8'h5b; 8'h3a: inv_sbox = 8'ha2; 8'h3b: inv_sbox = 8'h49;
                8'h3c: inv_sbox = 8'h6d; 8'h3d: inv_sbox = 8'h8b; 8'h3e: inv_sbox = 8'hd1; 8'h3f: inv_sbox = 8'h25;
                8'h40: inv_sbox = 8'h72; 8'h41: inv_sbox = 8'hf8; 8'h42: inv_sbox = 8'hf6; 8'h43: inv_sbox = 8'h64;
                8'h44: inv_sbox = 8'h86; 8'h45: inv_sbox = 8'h68; 8'h46: inv_sbox = 8'h98; 8'h47: inv_sbox = 8'h16;
                8'h48: inv_sbox = 8'hd4; 8'h49: inv_sbox = 8'ha4; 8'h4a: inv_sbox = 8'h5c; 8'h4b: inv_sbox = 8'hcc;
                8'h4c: inv_sbox = 8'h5d; 8'h4d: inv_sbox = 8'h65; 8'h4e: inv_sbox = 8'hb6; 8'h4f: inv_sbox = 8'h92;
                8'h50: inv_sbox = 8'h6c; 8'h51: inv_sbox = 8'h70; 8'h52: inv_sbox = 8'h48; 8'h53: inv_sbox = 8'h50;
                8'h54: inv_sbox = 8'hfd; 8'h55: inv_sbox = 8'hed; 8'h56: inv_sbox = 8'hb9; 8'h57: inv_sbox = 8'hda;
                8'h58: inv_sbox = 8'h5e; 8'h59: inv_sbox = 8'h15; 8'h5a: inv_sbox = 8'h46; 8'h5b: inv_sbox = 8'h57;
                8'h5c: inv_sbox = 8'ha7; 8'h5d: inv_sbox = 8'h8d; 8'h5e: inv_sbox = 8'h9d; 8'h5f: inv_sbox = 8'h84;
                8'h60: inv_sbox = 8'h90; 8'h61: inv_sbox = 8'hd8; 8'h62: inv_sbox = 8'hab; 8'h63: inv_sbox = 8'h00;
                8'h64: inv_sbox = 8'h8c; 8'h65: inv_sbox = 8'hbc; 8'h66: inv_sbox = 8'hd3; 8'h67: inv_sbox = 8'h0a;
                8'h68: inv_sbox = 8'hf7; 8'h69: inv_sbox = 8'he4; 8'h6a: inv_sbox = 8'h58; 8'h6b: inv_sbox = 8'h05;
                8'h6c: inv_sbox = 8'hb8; 8'h6d: inv_sbox = 8'hb3; 8'h6e: inv_sbox = 8'h45; 8'h6f: inv_sbox = 8'h06;
                8'h70: inv_sbox = 8'hd0; 8'h71: inv_sbox = 8'h2c; 8'h72: inv_sbox = 8'h1e; 8'h73: inv_sbox = 8'h8f;
                8'h74: inv_sbox = 8'hca; 8'h75: inv_sbox = 8'h3f; 8'h76: inv_sbox = 8'h0f; 8'h77: inv_sbox = 8'h02;
                8'h78: inv_sbox = 8'hc1; 8'h79: inv_sbox = 8'haf; 8'h7a: inv_sbox = 8'hbd; 8'h7b: inv_sbox = 8'h03;
                8'h7c: inv_sbox = 8'h01; 8'h7d: inv_sbox = 8'h13; 8'h7e: inv_sbox = 8'h8a; 8'h7f: inv_sbox = 8'h6b;
                8'h80: inv_sbox = 8'h3a; 8'h81: inv_sbox = 8'h91; 8'h82: inv_sbox = 8'h11; 8'h83: inv_sbox = 8'h41;
                8'h84: inv_sbox = 8'h4f; 8'h85: inv_sbox = 8'h67; 8'h86: inv_sbox = 8'hdc; 8'h87: inv_sbox = 8'hea;
                8'h88: inv_sbox = 8'h97; 8'h89: inv_sbox = 8'hf2; 8'h8a: inv_sbox = 8'hcf; 8'h8b: inv_sbox = 8'hce;
                8'h8c: inv_sbox = 8'hf0; 8'h8d: inv_sbox = 8'hb4; 8'h8e: inv_sbox = 8'he6; 8'h8f: inv_sbox = 8'h73;
                8'h90: inv_sbox = 8'h96; 8'h91: inv_sbox = 8'hac; 8'h92: inv_sbox = 8'h74; 8'h93: inv_sbox = 8'h22;
                8'h94: inv_sbox = 8'he7; 8'h95: inv_sbox = 8'had; 8'h96: inv_sbox = 8'h35; 8'h97: inv_sbox = 8'h85;
                8'h98: inv_sbox = 8'he2; 8'h99: inv_sbox = 8'hf9; 8'h9a: inv_sbox = 8'h37; 8'h9b: inv_sbox = 8'he8;
                8'h9c: inv_sbox = 8'h1c; 8'h9d: inv_sbox = 8'h75; 8'h9e: inv_sbox = 8'hdf; 8'h9f: inv_sbox = 8'h6e;
                8'ha0: inv_sbox = 8'h47; 8'ha1: inv_sbox = 8'hf1; 8'ha2: inv_sbox = 8'h1a; 8'ha3: inv_sbox = 8'h71;
                8'ha4: inv_sbox = 8'h1d; 8'ha5: inv_sbox = 8'h29; 8'ha6: inv_sbox = 8'hc5; 8'ha7: inv_sbox = 8'h89;
                8'ha8: inv_sbox = 8'h6f; 8'ha9: inv_sbox = 8'hb7; 8'haa: inv_sbox = 8'h62; 8'hab: inv_sbox = 8'h0e;
                8'hac: inv_sbox = 8'haa; 8'had: inv_sbox = 8'h18; 8'hae: inv_sbox = 8'hbe; 8'haf: inv_sbox = 8'h1b;
                8'hb0: inv_sbox = 8'hfc; 8'hb1: inv_sbox = 8'h56; 8'hb2: inv_sbox = 8'h3e; 8'hb3: inv_sbox = 8'h4b;
                8'hb4: inv_sbox = 8'hc6; 8'hb5: inv_sbox = 8'hd2; 8'hb6: inv_sbox = 8'h79; 8'hb7: inv_sbox = 8'h20;
                8'hb8: inv_sbox = 8'h9a; 8'hb9: inv_sbox = 8'hdb; 8'hba: inv_sbox = 8'hc0; 8'hbb: inv_sbox = 8'hfe;
                8'hbc: inv_sbox = 8'h78; 8'hbd: inv_sbox = 8'hcd; 8'hbe: inv_sbox = 8'h5a; 8'hbf: inv_sbox = 8'hf4;
                8'hc0: inv_sbox = 8'h1f; 8'hc1: inv_sbox = 8'hdd; 8'hc2: inv_sbox = 8'ha8; 8'hc3: inv_sbox = 8'h33;
                8'hc4: inv_sbox = 8'h88; 8'hc5: inv_sbox = 8'h07; 8'hc6: inv_sbox = 8'hc7; 8'hc7: inv_sbox = 8'h31;
                8'hc8: inv_sbox = 8'hb1; 8'hc9: inv_sbox = 8'h12; 8'hca: inv_sbox = 8'h10; 8'hcb: inv_sbox = 8'h59;
                8'hcc: inv_sbox = 8'h27; 8'hcd: inv_sbox = 8'h80; 8'hce: inv_sbox = 8'hec; 8'hcf: inv_sbox = 8'h5f;
                8'hd0: inv_sbox = 8'h60; 8'hd1: inv_sbox = 8'h51; 8'hd2: inv_sbox = 8'h7f; 8'hd3: inv_sbox = 8'ha9;
                8'hd4: inv_sbox = 8'h19; 8'hd5: inv_sbox = 8'hb5; 8'hd6: inv_sbox = 8'h4a; 8'hd7: inv_sbox = 8'h0d;
                8'hd8: inv_sbox = 8'h2d; 8'hd9: inv_sbox = 8'he5; 8'hda: inv_sbox = 8'h7a; 8'hdb: inv_sbox = 8'h9f;
                8'hdc: inv_sbox = 8'h93; 8'hdd: inv_sbox = 8'hc9; 8'hde: inv_sbox = 8'h9c; 8'hdf: inv_sbox = 8'hef;
                8'he0: inv_sbox = 8'ha0; 8'he1: inv_sbox = 8'he0; 8'he2: inv_sbox = 8'h3b; 8'he3: inv_sbox = 8'h4d;
                8'he4: inv_sbox = 8'hae; 8'he5: inv_sbox = 8'h2a; 8'he6: inv_sbox = 8'hf5; 8'he7: inv_sbox = 8'hb0;
                8'he8: inv_sbox = 8'hc8; 8'he9: inv_sbox = 8'heb; 8'hea: inv_sbox = 8'hbb; 8'heb: inv_sbox = 8'h3c;
                8'hec: inv_sbox = 8'h83; 8'hed: inv_sbox = 8'h53; 8'hee: inv_sbox = 8'h99; 8'hef: inv_sbox = 8'h61;
                8'hf0: inv_sbox = 8'h17; 8'hf1: inv_sbox = 8'h2b; 8'hf2: inv_sbox = 8'h04; 8'hf3: inv_sbox = 8'h7e;
                8'hf4: inv_sbox = 8'hba; 8'hf5: inv_sbox = 8'h77; 8'hf6: inv_sbox = 8'hd6; 8'hf7: inv_sbox = 8'h26;
                8'hf8: inv_sbox = 8'he1; 8'hf9: inv_sbox = 8'h69; 8'hfa: inv_sbox = 8'h14; 8'hfb: inv_sbox = 8'h63;
                8'hfc: inv_sbox = 8'h55; 8'hfd: inv_sbox = 8'h21; 8'hfe: inv_sbox = 8'h0c; 8'hff: inv_sbox = 8'h7d;
                default: inv_sbox = word;
            endcase
        end
    endfunction
    
    // 현재 키(current_key)를 바탕으로 이전 라운드 키(prev_key)를 구하는 inv_key_expansion
    task inv_key_expansion(input [127:0] current_key, output [127:0] prev_key);
        reg [31:0] temp;
        reg [31:0] w [0:3];
        integer i;
    
         begin
            // 현재 키를 32비트 단위로 나눠 w0, w1, w2, w3에 저장 (각각 8비트)
            w[0] = current_key[127:96];
            w[1] = current_key[95:64];
            w[2] = current_key[63:32];
            w[3] = current_key[31:0];
    
            w[3] = w[3] ^ w[2]; 
            w[2] = w[2] ^ w[1];
            w[1] = w[1] ^ w[0];
    
    
            // w[0] 역변환과정
            temp = w[3];
            temp = RotWord(temp);
            temp = SubWord(temp);
            
            temp = temp ^ Rcon[round + 1];
            temp = temp ^ w[0];
    
            w[0] = temp;
    
            // 이전 라운드 키를 생성하여 출력
            prev_key = {w[0], w[1], w[2], w[3]};
        end
    endtask
    
    
    // InvSubBytes
    task inv_sub_bytes;
        integer i;
        begin
            for (i = 0; i < 128; i = i + 8) begin
                state[i +: 8] = inv_sbox(state[i +: 8]);
            end
        end
    endtask
    
    
    // InvShiftRows
    task inv_shift_rows;
        reg [127:0] temp;
        begin
            temp[127:120] = state[127:120];
            temp[95:88]   = state[95:88];
            temp[63:56]   = state[63:56];
            temp[31:24]   = state[31:24];
     
            // 두 번째 행: 오른쪽으로 1바이트 이동
            temp[119:112] = state[23:16];
            temp[87:80]   = state[119:112];
            temp[55:48]   = state[87:80];
            temp[23:16]   = state[55:48];
    
            // 세 번째 행: 오른쪽으로 2바이트 이동
            temp[111:104] = state[47:40];
            temp[79:72]   = state[15:8];
            temp[47:40]   = state[111:104];
            temp[15:8]    = state[79:72];
    
            // 네 번째 행: 오른쪽으로 3바이트 이동 
            temp[103:96] = state[71:64];
            temp[71:64]  = state[39:32];
            temp[39:32]  = state[7:0];
            temp[7:0]    = state[103:96];
    
            state = temp;
        end
    endtask
    
    // invmixcolumns을 위한 함수들 mul9, mul11, mul13, mul14
    function [7:0] mul9;
        input [7:0] a;
        begin
            mul9 = mul2(mul2(mul2(a))) ^ a; // 9 = 2^3 * a + a
        end
    endfunction
    
    function [7:0] mul11;
        input [7:0] a;
        begin
            mul11 = mul2(mul2(mul2(a)) ^ a) ^ a; // 11 = 2^3 * a + 2 * a + a
        end
    endfunction
    
    function [7:0] mul13;
        input [7:0] a;
        begin
            mul13 = mul2(mul2(mul2(a) ^ a)) ^ a; // 13 = 2^3 * a + 2^2 * a + a
        end
    endfunction
    
    function [7:0] mul14;
        input [7:0] a;
        begin
            mul14 = mul2(mul2(mul2(a) ^ a) ^ a); // 14 = 2^3 * a + 2^2 * a + 2 * a
        end
    endfunction
    
     // InvMixColumns, Galois Field에서의 다항식 곱셈 사용해 역변환
    task inv_mix_columns;
        integer i;
        reg [7:0] s0, s1, s2, s3;       // column의 각 바이트를 저장
        reg [7:0] out0, out1, out2, out3; // output
        begin
            for (i = 0; i < 4; i = i + 1) 
            begin
                // 각 열의 4바이트 데이터를 추출
                s0 = state[127 - 32*i -: 8];
                s1 = state[119 - 32*i -: 8];
                s2 = state[111 - 32*i -: 8];
                s3 = state[103 - 32*i -: 8];
    
                // Galois Field에서 다항식 곱셈과 XOR 연산 수행
                // 역변환 결과를 각 바이트에 저장
                out0 = mul14(s0) ^ mul11(s1) ^ mul13(s2) ^ mul9(s3);
                out1 = mul9(s0) ^ mul14(s1) ^ mul11(s2) ^ mul13(s3);
                out2 = mul13(s0) ^ mul9(s1) ^ mul14(s2) ^ mul11(s3);
                out3 = mul11(s0) ^ mul13(s1) ^ mul9(s2) ^ mul14(s3);
    
                // 변환된 바이트를 state에 저장
                state[127 - 32*i -: 8] = out0;
                state[119 - 32*i -: 8] = out1;
                state[111 - 32*i -: 8] = out2;
                state[103 - 32*i -: 8] = out3;
            end
        end
    endtask



    
    // 암호화 및 복호화 실행 제어
    always @(posedge CLK or negedge nRST)
        begin
            if (!nRST) // nRST의 negedge에 초기화
            begin
                i <= 0;
                DONE <= 0;
                TEXTOUT <= 128'b0;
                state <= 128'b0;
                round_key <= 128'b0;
                round <= 0;
                running <= 0;
            end 
            else if (START) // 시작 신호가 들어왔을 때
            begin
                if(!running) // 현재 암호화 및 복호화가 실행중인 상태가 아닌경우 
                begin // 초기화
                    DONE <= 0;
                    state <= TEXTIN;
                    round_key <= KEY;
                    round <= 0;
                    running <= 1; // 실행중인 상태로 변경
                end
                
                // running = 1 일 때 중간에 START 신호 들어오면 무시
                //$display("round_key : %h", temp_round_key[i]);
            end 
   
        end //always(posedge CLK or negedge nRST)문 end
        
     integer i; // 반복문용 변수       
    always @(posedge CLK or posedge running)
        begin
        if (!running)
            DONE <= 0; // done hold for 1 cycle을 위해
        else if (running) // 암호화 및 복호화가 실행중인 상태, (running = 1 일 때 중간에 START 신호 들어와도 무시하기 위해)
            begin // 암호화 및 복호화 step
                //---------------------------------------------------------------------------------------------------------
                if (ENCDEC == 0) // 암호화
                begin
                    if (round == 0)
                    begin
                        add_round_key(round_key); // 초기 AddRoundKey
                    end 
                    else if (round < 10)
                    begin
                        sub_bytes;
                        shift_rows;
                        mix_columns;
                        key_expansion(round_key, round_key);
                        add_round_key(round_key);
                    end 
                    else if (round == 10) 
                    begin
                        sub_bytes;
                        shift_rows;
                        key_expansion(round_key, round_key);
                        
                        add_round_key(round_key);
                        TEXTOUT <= state; // 암호화 완료 상태 저장
                
                        DONE <= 1;        // 완료 신호
                        running <= 0; // 종료
                    end
                    round <= round + 1;
                end
                
                //-------------------------------------------------------------------------------------------------------------
                
                else // 복호화
                    if(i < 10) // round 10의 round key 먼저 구하기
                    begin 
                        round = round + 1;
                        key_expansion(round_key, round_key);
                        i = i + 1;
                    end
    
                    // 복호화 step 시작
                    if(i == 10)
                    begin // round 10에서 역순으로 시작
                        if (round == 10) 
                        begin
                            add_round_key(round_key); // 초기 AddRoundKey (roundkey: 10)
          
                        end 
                        
                        else if (round > 0)
                        begin
                            inv_shift_rows;
                            inv_sub_bytes;
                            inv_key_expansion(round_key, round_key);
                            add_round_key(round_key);
                            inv_mix_columns;
                        end 
                        
                        else if (round == 0) 
                        begin
                            inv_shift_rows;
                            inv_sub_bytes;
                            inv_key_expansion(round_key, round_key);
                            add_round_key(round_key);
                            TEXTOUT <= state; // 복호화 완료 상태 저장
                            DONE <= 1;        // 완료 신호
                            running <= 0; // 종료
                        end
                        round <= round - 1;
                    end
                end
        end

   

endmodule

 

 

 

그리고 이어서 아래는 Stimulus (테스트벤치) 코드이다.

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/11/09 19:52:33
// Design Name: 
// Module Name: Top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module Top;


    reg clk, nrst, encdec, start;
    reg [127:0] key;
    reg [127:0] textin;
    wire [127:0] textout;
    wire done;

    AES m1 (.CLK(clk), .nRST(nrst), .ENCDEC(encdec), .START(start), .KEY(key), .TEXTIN(textin), .TEXTOUT(textout), .DONE(done));

    initial begin
        clk = 0;
        forever #5 clk = ~clk; // 10 ns 주기 클럭
    end

    // 테스트
    initial begin
        // 초기화
        nrst = 0;
        encdec = 0; // 0: 암호화 모드
        start = 0;
        key = 128'h2b7e151628aed2a6abf7158809cf4f3c; // 예시 키
        textin = 128'h3243f6a8885a308d313198a2e0370734; // 예시 평문

        // 리셋 후 AES 시작
        #10 nrst = 1;
        #10 start= 1; // START 신호로 암호화 시작
        #10 start = 0; // START 신호 해제
        
        #10 start = 1; // 프로세스 중간에 start 신호가 들어와도 이상이 없는지 확인을 위한 test
        #10 start = 0;
        
        // 암호화 완료 대기 후 결과 출력
        wait(done == 1);
        $display($time, " Plain Text  : %h", textin);
        $display($time, " Key         : %h", key);
        $display($time," Encrypted Text : %h", textout);


        // 초기화
        #10 nrst = 0;
        encdec = 1; //복호화
        start = 0;
        key = 128'h2b7e151628aed2a6abf7158809cf4f3c;
        textin = textout;


        //복호화 프로세스 시작
        #10 nrst = 1;
        #10 start = 1; // START 신호로 복호화 시작
        #10 start = 0; // START 신호 해제

        #10 start = 1; // 프로세스 중간에 start 신호가 들어와도 이상이 없는지 확인을 위한 test
        #10 start = 0;

        #10 start = 1; // 프로세스 중간에 start 신호가 들어와도 이상이 없는지 확인을 위한 test
        #10 start = 0;

        wait(done == 1);
        $display($time," Decrypted Text : %h", textout);


        #20 $stop;
    end
endmodule

 

Stimulus로 테스트한 waveform 결과이다.

 

간단화된 Datapath

 

Finite State Machine

 

암호화

TEXTIN과 KEY, ENCDEC = 0 으로 주어지고 START 신호가 들어온다. 이 때 AES 암호화 프로세스가 시작된다.

가장 초기 라운드로 Addroundkey를 한번 하고, main encrypt round(subbyte-shiftrow-mixcolumns-addroundkey)를 총 라운드 1부터 라운드 9까지 9번 반복한다.

라운드 10, 즉 마지막 encrypt round는 mixcolumns 단계를 생략한다.

라운드 10까지 모두 완료하면 DONE에 1 신호와, OUTPUT에 암호문이 출력된다.

 

복호화

마찬가지로 TEXTIN과 KEY, ENCDEC = 1 으로 주어지고 START 신호가 들어온다. 이 때 AES 복호화 프로세스가 시작된다.

이 때 다른 점은 우선 key_expansion을 10회 반복해 입력 key를 마지막 round 10 key로 먼저 만들고 시작한다.

이유는 분해는 조립의 역순이듯 복호화도 암호화의 역순이기 때문에, 초기 키를 암호화 단계의 마지막 키로 설정하는 것이다. (암호화할 때 사용했던 10개의 key를 모두 배열에 저장해 복호화에 사용하면 이 과정에 소요되는 10번의 clock cycle을 줄일 수 있을 것이다. 하지만 프로젝트의 조건은 key를 배열에 저장하면 안된다라는 조건이 있었다)

그래서 round 10 key로 addroundkey를 하는 초기연산을 한 후, main decrypt round(inv_shift_rows-inv_sub_bytes-addroundkey-inv_mixcolumns)를 round 1부터 9까지 총 9회 반복한다.마지막 10번째 decrypt 라운드로는 역시 inv_mixcolumns는 생략한다.

이 10개의 라운드가 모두 완료되면 DONE에 1 신호와, OUTPUT에 복호문이 출력된다.

 

Verilog에서 실행해보면 Plain text와 Decrypted text가 일치해 암호화 및 복호화가 잘 수행되었음을 확인할 수 있다