Verilog 프로젝트로, 128비트 입력 및 키가 주어졌을 때의 AES 암호화 및 복호화 알고리즘을 구현했다.
AES 암호화란 간단하게 설명하자면 입력으로 암호화할 텍스트와 키를 받고, Subbyte - Shiftrows - Mixcolumns - Addroundkey라는 암호화 스테이지를 10번 반복해서 암호화하는 알고리즘이다.
그래서 각각의 암호화 스테이지를 함수(task 및 function)으로 미리 구현하여 사용하였다.

아래 코드는 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로 변환하여 출력한다.

위 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



암호화
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에 복호문이 출력된다.
