본문 바로가기
Data Structure & Algorithm/알고리즘

[암호] AES (C++ 구현/private 함수) - 3

by 별준 2021. 9. 25.

2021.09.23 - [Data Structure & Algorithm/알고리즘] - [암호] AES (C++ 구현/멤버 변수, public 함수) - 2

 

[암호] AES (C++ 구현/멤버 변수, public 함수) - 2

2021.09.23 - [Data Structure & Algorithm/알고리즘] - [암호] AES (Advanced Encryption Standard) - 1 [암호] AES (Advanced Encryption Standard) - 1 References 리얼월드 알고리즘 Contents AES란? AES 암호..

junstar92.tistory.com

이전 글에 이어서 AES Class의 private 멤버 함수들에 대해서 살펴보겠습니다.

내부에서 변환/출력 용으로 사용되는 함수를 제외하고, 실제 AES 알고리즘이 구현된 함수들 위주로 살펴보겠습니다.

class AES
{
private:
	...
    
	std::vector<byte> _createState(const std::vector<byte>& block, uint32_t start);
	std::vector<byte> _expandKey(const std::vector<byte>& cipherKey, bool verbose);
	void _addRoundKey(std::vector<byte>& state, const std::vector<byte>& roundKey, uint8_t round, bool verbose);

	void _xor_iv(std::vector<byte>& state, std::vector<byte>& iv, bool verbose);

	void _encryption(std::vector<byte>& state, const std::vector<byte>& roundKey, bool verbose);
	byte _mappingSBox(const byte val);
	void _subBytes(std::vector<byte>& state, bool verbose);
	void _shiftRows(std::vector<byte>& statee, bool verbose);
	void _mixColumns(std::vector<byte>& state, bool verbose);

	void _decryption(std::vector<byte>& state, const std::vector<byte>& roundKey, bool verbose);
	byte _mappingInvSBox(const byte val);
	void _invSubBytes(std::vector<byte>& state, bool verbose);
	void _invShiftRows(std::vector<byte>& state, bool verbose);
	void _invMixColumns(std::vector<byte>& state, bool verbose);
	
	std::string _convertTypeByteStateToStr(const std::vector<byte>& state, bool decryption);
	std::vector<byte> _convertTypeStrToByteBlock(std::string str);
	void _printState(const std::vector<byte>& state);
	std::string _convertCharToStrHex(const char& c);
	char _convertHexToChar(const byte hex);

public:
	...
};

 

그리고, 이전 글에서 말씀드렸듯이 AES 클래스에서 사용되는 state matrix는 2차원 배열이 아닌 1차원 배열(vector)로 4x4 matrix를 표현합니다.


private 함수

_createState

std::vector<byte> AES::_createState(const std::vector<byte>& block, uint32_t start)
{
	uint8_t last = std::min(this->Nstate_, static_cast<uint8_t>(block.size() - start));
	byte init_val = 0;
	if (last != this->Nstate_)
		init_val = this->Nstate_ - last; // using PKCS#7 padding
	std::vector<byte> ret(this->Nstate_, init_val);

	for (uint8_t i = start; i < start + last; i++) {
		ret[i - start] = block[i];
	}

	return ret;
}

입력받은 평문 또는 암호문으로 4x4 state matrix를 추출하는 함수입니다. 파라미터로 state matrix로 추출할 시작점(start)도 입력받아서, start 지점부터 16바이트를 입력받은 평문/암호문에서 읽습니다. (line 9 ~ 11)

 

평문인 경우에는 마지막 블록이 항상 16바이트로 떨어지지 않기 때문에, 마지막 위치가 start + 16보다 작은지 확인해주어야 합니다. (line 3)

그리고, 마지막 블록이 16바이트보다 작다면, PKCS#7 padding을 사용해서 나머지 부분은 남은 부분의 개수를 값으로 채우게 됩니다. (line 5 ~ 7)

 

_expandKey

라운드키를 생성하는 KeyExpansion 연산입니다. 입력받은 암호화키로 RotWord, SubWord, XOR(rcon) 연산을 통해서 키를 확장합니다. 자세한 알고리즘은 이전 글의 Key Schedule을 참조하시기 바랍니다 !

2021.09.23 - [Data Structure & Algorithm/알고리즘] - [암호] AES (Advanced Encryption Standard) - 1

std::vector<byte> AES::_expandKey(const std::vector<byte>& cipherKey, bool verbose)
{
	// expand key : Nstate x (Nround + 1) array
	std::vector<byte> roundKey(this->Nstate_ * (this->Nround_ + 1), 0);

	for (uint8_t i = 0; i < this->Nkey_; i++)
	{
		roundKey[(i * this->Ncol_) + 0] = cipherKey[(i * this->Ncol_) + 0];
		roundKey[(i * this->Ncol_) + 1] = cipherKey[(i * this->Ncol_) + 1];
		roundKey[(i * this->Ncol_) + 2] = cipherKey[(i * this->Ncol_) + 2];
		roundKey[(i * this->Ncol_) + 3] = cipherKey[(i * this->Ncol_) + 3];
	}

	for (uint8_t i = this->Nkey_; i < this->Ncol_ * (this->Nround_ + 1); i++)
	{
		byte Wi_1[4];
		uint8_t i_1 = (i - 1) * 4;

		Wi_1[0] = roundKey[i_1 + 0];
		Wi_1[1] = roundKey[i_1 + 1];
		Wi_1[2] = roundKey[i_1 + 2];
		Wi_1[3] = roundKey[i_1 + 3];

		if (i % this->Nkey_ == 0) {
			// RotWords
			byte temp = Wi_1[0];
			Wi_1[0] = Wi_1[1];
			Wi_1[1] = Wi_1[2];
			Wi_1[2] = Wi_1[3];
			Wi_1[3] = temp;

			// SubBytes
			Wi_1[0] = this->_mappingSBox(Wi_1[0]);
			Wi_1[1] = this->_mappingSBox(Wi_1[1]);
			Wi_1[2] = this->_mappingSBox(Wi_1[2]);
			Wi_1[3] = this->_mappingSBox(Wi_1[3]);

			// XOR Rcon
			Wi_1[0] = Wi_1[0] ^ rcon_[i / this->Nkey_];
		}

		if (this->Nkey_ == 8) // if AES256
		{
			if (i % this->Nkey_ == 4)
			{
				Wi_1[0] = this->_mappingSBox(Wi_1[0]);
				Wi_1[1] = this->_mappingSBox(Wi_1[1]);
				Wi_1[2] = this->_mappingSBox(Wi_1[2]);
				Wi_1[3] = this->_mappingSBox(Wi_1[3]);
			}
		}

		uint8_t i_4 = (i - this->Nkey_) * 4;
		roundKey[(i * this->Ncol_) + 0] = roundKey[i_4 + 0] ^ Wi_1[0];
		roundKey[(i * this->Ncol_) + 1] = roundKey[i_4 + 1] ^ Wi_1[1];
		roundKey[(i * this->Ncol_) + 2] = roundKey[i_4 + 2] ^ Wi_1[2];
		roundKey[(i * this->Ncol_) + 3] = roundKey[i_4 + 3] ^ Wi_1[3];
	}

	if (verbose) {
		for (uint8_t i = 0; i < this->Nround_ + 1; i++) {
			printf("\n----- Round Key %d ----\n", i);

			for (uint8_t r = 0; r < this->Bsize_; r++)
			{
				printf("      ");
				for (uint8_t c = 0; c < this->Ncol_; c++)
				{
					printf("%02X ", roundKey[(i * this->Nstate_) + (c * this->Ncol_) + r]);
				}
				printf("\n");
			}
		}
	}

	return roundKey;
}

부분부분 나누어서 알아보겠습니다.

std::vector<byte> roundKey(this->Nstate_ * (this->Nround_ + 1), 0);

roundKey를 저장할 vector를 선언합니다. 이때, 사용되는 roundKey는 4x4(Nstate_) matrix가 (round 수 + 1)개 필요합니다. round 수보다 하나 더 필요한 것은 처음 사용되는 round가 시작되기 전에 암호화키가 사용되기 때문입니다. 

구해야되는 라운드키의 크기는 AES128인 경우에는 4x4x11, AES192는 4x4x13, AES256은 4x4x15 바이트입니다.

 

첫 4x4 matrix의 값은 입력받은 암호화키가 되고 나머지 라운드의 키를 KeyExpand 연산을 통해서 구합니다.

생성된 라운드키의 인덱스는 아래처럼 위치하게 됩니다.

for (uint8_t i = 0; i < this->Nkey_; i++)
{
	roundKey[(i * this->Ncol_) + 0] = cipherKey[(i * this->Ncol_) + 0];
	roundKey[(i * this->Ncol_) + 1] = cipherKey[(i * this->Ncol_) + 1];
	roundKey[(i * this->Ncol_) + 2] = cipherKey[(i * this->Ncol_) + 2];
	roundKey[(i * this->Ncol_) + 3] = cipherKey[(i * this->Ncol_) + 3];
}

위 코드에서 첫 라운드키를 입력받은 암호화키로 설정하게 됩니다. 하나의 word씩 진행하여 암호화키의 word 수 만큼 복사를 진행합니다.

for (uint8_t i = this->Nkey_; i < this->Ncol_ * (this->Nround_ + 1); i++)

그리고 for문을 통해서 하나의 word씩 라운드키를 확장해나가게 됩니다. AES128인 경우에는 4번 word인 16,17,18,19부터 채우기 시작합니다.

키 확장은 아래의 식으로 이루어집니다.

복잡해보이지만 간단합니다. 위 식에서 i는 for문에서의 i와 동일하며, 위 식의 N은 this->Nkey_에 매칭됩니다.

따라서, 0 ~ 3 까지는 입력받은 암호화키가 그대로 입력되고, i = 4부터 진행됩니다.

i = 4인 경우 위 식에서 2번째에 해당됩니다. 따라서 1만큼 앞에 있는 i = 3의 값에 RotWord 연산 그리고 SubWord 연산을 수행하고, rcon과 XOR연산을 수행합니다. 그리고 i = 4에서 N만큼(AES128인 경우에는 4겠지요) 앞에 있는 i = 0의 값과 XOR 연산을 수행하게 됩니다. (rcon은 round constant 입니다.) 

 

구현은 아래와 같습니다. 값을 저장할 변수인 Wi_1을 선언하고, 1만큼 앞에 있는 값을 복사해서 연산을 수행하면서 이 변수에 저장합니다.

byte Wi_1[4];
uint8_t i_1 = (i - 1) * 4;

Wi_1[0] = roundKey[i_1 + 0];
Wi_1[1] = roundKey[i_1 + 1];
Wi_1[2] = roundKey[i_1 + 2];
Wi_1[3] = roundKey[i_1 + 3];

if (i % this->Nkey_ == 0) {
	// RotWords
	byte temp = Wi_1[0];
	Wi_1[0] = Wi_1[1];
	Wi_1[1] = Wi_1[2];
	Wi_1[2] = Wi_1[3];
	Wi_1[3] = temp;

	// SubBytes
	Wi_1[0] = this->_mappingSBox(Wi_1[0]);
	Wi_1[1] = this->_mappingSBox(Wi_1[1]);
	Wi_1[2] = this->_mappingSBox(Wi_1[2]);
	Wi_1[3] = this->_mappingSBox(Wi_1[3]);

	// XOR Rcon
	Wi_1[0] = Wi_1[0] ^ rcon_[i / this->Nkey_];
}

 

만약 i = 5라면 위 식에서 4번째 경우에 해당되며, RotWord, SubBytes, XOR Rcon 연산은 수행되지 않고, 단순히 이전 값(i = 4)과 N만큼 앞에 있는 i = 1의 값을 XOR 연산한 결과가 됩니다.

i = 6, 7도 마찬가지로 4번째 경우에 해당됩니다.

 

위 식에서 세 번째 경우는 오직 AES256에서만 적용되는 경우입니다. 세 번째 경우의 조건을 만족하면 SubBytes 연산만 수행됩니다.

if (this->Nkey_ == 8) // if AES256
{
	if (i % this->Nkey_ == 4)
	{
		Wi_1[0] = this->_mappingSBox(Wi_1[0]);
		Wi_1[1] = this->_mappingSBox(Wi_1[1]);
		Wi_1[2] = this->_mappingSBox(Wi_1[2]);
		Wi_1[3] = this->_mappingSBox(Wi_1[3]);
	}
}

그리고 마지막으로 공통적으로 포함되는 N 만큼 앞에 있는 값과의 XOR 연산을 최종적으로 수행하면 라운드키의 한 word 값을 구하게 됩니다.

uint8_t i_4 = (i - this->Nkey_) * 4;
roundKey[(i * this->Ncol_) + 0] = roundKey[i_4 + 0] ^ Wi_1[0];
roundKey[(i * this->Ncol_) + 1] = roundKey[i_4 + 1] ^ Wi_1[1];
roundKey[(i * this->Ncol_) + 2] = roundKey[i_4 + 2] ^ Wi_1[2];
roundKey[(i * this->Ncol_) + 3] = roundKey[i_4 + 3] ^ Wi_1[3];

 

이전 글에서 키확장에 사용되었던 암호화키를 가지고 키확장을 수행하면 다음의 라운드키를 얻을 수 있습니다.

 

_addRoundKey

void AES::_addRoundKey(std::vector<byte>& state, const std::vector<byte>& roundKey, uint8_t round, bool verbose)
{
	for (uint8_t i = 0; i < this->Nstate_; i++) {
		state[i] ^= roundKey[(this->Nstate_ * round) + i];
	}

	if (verbose) {
		printf("\n----- After Round %d -----\n", round);
		this->_printState(state);
	}
}

단순히 XOR 연산만 하는 함수이며, state matrix와 roundKey의 XOR 연산을 수행합니다. 파라미터로 몇 번째 라운드인지 입력받아서 해당 라운드에 올바른 라운드키를 사용하여 XOR 연산을 수행하도록 합니다.

 

_subBytes

byte AES::_mappingSBox(const byte val)
{
	return this->sbox_[val];
}
void AES::_subBytes(std::vector<byte>& state, bool verbose)
{
	for (uint8_t i = 0; i < this->Nstate_; i++) {
		state[i] = this->_mappingSBox(state[i]);
	}

	if (verbose) {
		printf("\n----- After SubBytes -----\n");
		this->_printState(state);
	}
}

SubBytes 연산을 수행하는 함수입니다. 내부 _mappingSBox 함수를 통해서 sbox에 매핑되는 값을 반환받고, 이 값으로 변환하게 됩니다.

 

_shiftRows

void AES::_shiftRows(std::vector<byte>& state, bool verbose)
{
	byte temp;
	// Rotate row 1
	temp = state[1];
	state[1] = state[5];
	state[5] = state[9];
	state[9] = state[13];
	state[13] = temp;

	// Rotate row 2
	temp = state[2];
	state[2] = state[10];
	state[10] = temp;
	temp = state[6];
	state[6] = state[14];
	state[14] = temp;

	// Roate row 3
	temp = state[3];
	state[3] = state[15];
	state[15] = state[11];
	state[11] = state[7];
	state[7] = temp;

	if (verbose) {
		printf("\n----- After ShiftRows -----\n");
		this->_printState(state);
	}
}

i번째 행을 i씩 왼쪽으로 shift하는 ShiftRows 연산입니다. 

 

_mixColumns

가장 구현하기 까다로웠던 함수였습니다. 아래처럼 고정된 4x4 행렬을 각 word 열과의 곱연산이 이 함수에 구현되어 있습니다. 

#define multiply(a) (((a) << 1) ^ (((a >> 7) & 1) * 0x1b))

void AES::_mixColumns(std::vector<byte>& state, bool verbose)
{
	std::vector<byte> temp(this->Nstate_, 0);
	const uint8_t fixed[] = { 2, 3, 1, 1, 1, 2, 3, 1, 1, 1, 2, 3, 3, 1, 1, 2 };

	for (uint8_t i = 0; i < this->Bsize_; i++) {
		for (uint8_t j = 0; j < this->Ncol_; j++) {
			for (uint8_t k = j * 4; k < (j * 4) + 4; k++) {
				if (fixed[k] == 1) {
					temp[(i * this->Ncol_) + j] ^= state[(i * this->Ncol_) + (k % 4)];
				}
				else {
					temp[(i * this->Ncol_) + j] ^= multiply(state[(i * this->Ncol_) + (k % 4)]);
					if (fixed[k] == 3) {
						temp[(i * this->Ncol_) + j] ^= state[(i * this->Ncol_) + (k % 4)];
					}
				}
			}
		}
	}

	for (uint8_t i = 0; i < this->Nstate_; i++) {
		state[i] = temp[i];
	}

	if (verbose) {
		printf("\n----- After MixColumns -----\n");
		this->_printState(state);
	}
}

행렬의 곱 연산이지만, 보통의 행렬 곱 연산과는 조금 다릅니다.

바이트 끼리의 곱은 아래의 공식으로 계산되고, 그 곱들의 합은 XOR 연산으로 계산됩니다.

여기서 \(2 \dots a\)의 계산을 편하게 하기 위해서 multiply라는 매크로를 미리 구현해두었습니다.

#define multiply(a) (((a) << 1) ^ (((a >> 7) & 1) * 0x1b))

7번째 비트가 1이라면 0b00011011과 XOR 연산이 추가로 수행됩니다.

 

mixColumns 연산은 3중 for문에서 수행됩니다. 각 위치에 맞는 고정된 행렬값이 temp 변수에 XOR연산으로 계속 계산되면서 저장됩니다. 연산되는 행렬 값이 1인 경우에는 바이트가 그대로 XOR 연산되고, 2인 경우에는 multiply 연산이 수행된 후에 XOR 연산이 되며, 3인 경우에는 multiply 연산 + XOR 연산이 모두 수행됩니다.

참고로 최적화 코드가 존재하며, 위의 코드는 행렬 연산을 최적화없이 그대로 구현하였습니다.
최적화 코드는 아래 경로에서 확인하실 수 있습니다.
https://en.wikipedia.org/wiki/Rijndael_MixColumns#Implementation_example

 


다음은 복호화 함수입니다.

_decryption

void AES::_decryption(std::vector<byte>& state, const std::vector<byte>& roundKey, bool verbose)
{
	this->_addRoundKey(state, roundKey, this->Nround_, verbose);
	for (uint8_t i = this->Nround_ - 1; i > 0; i--) {
		this->_invShiftRows(state, verbose);
		this->_invSubBytes(state, verbose);
		this->_addRoundKey(state, roundKey, i, verbose);
		this->_invMixColumns(state, verbose);
	}
	this->_invShiftRows(state, verbose);
	this->_invSubBytes(state, verbose);
	this->_addRoundKey(state, roundKey, 0, verbose);
}

복호화 함수는 암호화 함수와 유사하지만, 연산 순서와 내부 구현이 아주 살짝 다릅니다.

AddRoundKey 연산은 동일하지만, 암호화와는 다르게 순서가 반대입니다. 즉 마지막 라운드키가 가장 먼저 사용되며, 첫 번째 라운드키, 즉, 암호화키가 마지막에 사용됩니다. (_addRoundKey 로직은 동일합니다.)

 

그리고 암호화와는 달리 ShiftRows 연산이 먼저 수행됩니다. 그리고 SubBytes 연산이 수행되며, 암호화와 달리 addRoundKey 연산이 수행되고 나서 MixColumns 연산이 수행됩니다. 마지막에 MixColumn 연산은 수행되지 않습니다.

미세하게 다른 로직은 각 함수들을 살펴보면서 말씀드리겠습니다.

 

_invShiftRows

복호화의 ShiftRows 연산입니다. 암호화때와는 i번째 행에서 i번째만큼 shift하는 것은 동일하지만 왼쪽이 아니라 오른쪽으로 Shift 됩니다.

void AES::_invShiftRows(std::vector<byte>& state, bool verbose)
{
	byte temp;
	// Rotate row 1
	temp = state[1];
	state[1] = state[13];
	state[13] = state[9];
	state[9] = state[5];
	state[5] = temp;

	// Rotate row 2
	temp = state[2];
	state[2] = state[10];
	state[10] = temp;

	temp = state[6];
	state[6] = state[14];
	state[14] = temp;

	// Roate row 3
	temp = state[3];
	state[3] = state[7];
	state[7] = state[11];
	state[11] = state[15];
	state[15] = temp;

	if (verbose) {
		printf("\n----- After Inverse ShiftRows -----\n");
		this->_printState(state);
	}
}

 

_invSubBytes

역 sbox와 매핑시켜주는 함수입니다. 내부의 _mappingInvSBox 함수가 값을 매핑시켜 반환해줍니다.

byte AES::_mappingInvSBox(const byte val)
{
	return this->inv_sbox_[val];
}

void AES::_invSubBytes(std::vector<byte>& state, bool verbose)
{
	for (uint8_t i = 0; i < this->Nstate_; i++) {
		state[i] = this->_mappingInvSBox(state[i]);
	}

	if (verbose) {
		printf("\n----- After Inverse SubBytes -----\n");
		this->_printState(state);
	}
}

 

_invMixColumns

암호화의 MixColumns 연산과 동일하지만 사용되는 고정 행렬이 다릅니다. 암호화에서 사용한 행렬의 역행렬이 사용되며, 이 행렬은 아래의 값을 가집니다.

\[\begin{bmatrix} 14 & 9 & 13 & 11\\ 11 & 14 & 9 & 13\\ 13 & 11 & 14 & 9\\ 9 & 13 & 11 & 14 \end{bmatrix}\]

void AES::_invMixColumns(std::vector<byte>& state, bool verbose)
{
	std::vector<byte> temp(this->Nstate_, 0);
	const uint8_t fixed[] = { 14, 11, 13, 9, 9, 14, 11, 13, 13, 9 ,14, 11, 11, 13, 9, 14 };

	for (uint8_t i = 0; i < this->Bsize_; i++) {
		for (uint8_t j = 0; j < this->Ncol_; j++) {
			for (uint8_t k = j * 4; k < (j * 4) + 4; k++) {
				temp[(i * this->Ncol_) + j] ^= multiply(multiply(multiply(state[(i * this->Ncol_) + (k % 4)])));

				if (fixed[k] == 9 || fixed[k] == 11 || fixed[k] == 13) {
					temp[(i * this->Ncol_) + j] ^= state[(i * this->Ncol_) + (k % 4)];
				}
				if (fixed[k] == 11 || fixed[k] == 14) {
					temp[(i * this->Ncol_) + j] ^= multiply(state[(i * this->Ncol_) + (k % 4)]);
				}
				if (fixed[k] == 13 || fixed[k] == 14) {
					temp[(i * this->Ncol_) + j] ^= multiply(multiply(state[(i * this->Ncol_) + (k % 4)]));
				}
			}
		}
	}

	for (uint8_t i = 0; i < this->Nstate_; i++) {
		state[i] = temp[i];
	}

	if (verbose) {
		printf("\n----- After Inverse MixColumns -----\n");
		this->_printState(state);
	}
}

각 바이트의 곱 연산은 아래처럼 계산됩니다.

\[\begin{align*}14 \cdot a&= 8 \cdot a \otimes 4 \cdot a \otimes 2 \cdot a\\  &= (2+2+2) \cdot a \otimes (2+2) \cdot a \otimes 2 \cdot a \\ &=  2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \end{align*}\]

즉, 14가 곱해지는 경우 multiply 연산이 총 6번 수행됩니다.

13이 곱해지는 경우는

\[\begin{align*}
13 \cdot a&= 8 \cdot a \otimes 4 \cdot a \otimes a\\
  &= (2+2+2) \cdot a \otimes (2+2) \cdot a \otimes  a \\
 &=  2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes  a
\end{align*}\]

11의 경우

\[\begin{align*}
11 \cdot a&= 8 \cdot a \otimes 2 \cdot a \otimes a\\
  &= (2+2+2) \cdot a \otimes 2 \cdot a \otimes  a \\
 &=  2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes  a
\end{align*}\]

9의 경우는

\[\begin{align*}
9 \cdot a&= 8 \cdot a \otimes a\\
  &= (2+2+2) \cdot a \otimes a \\
 &=  2 \cdot a \otimes 2 \cdot a \otimes 2 \cdot a \otimes  a
\end{align*}\]

로 계산됩니다.

 

각 연산 횟수만큼 수행되도록 구현되어 있으며, 나머지 구현은 암호화와 동일합니다.

 


이렇게 구현된 클래스는 아래처럼 사용할 수 있습니다.

#include <iostream>
#include "AES.hpp"

int main(void) {
	AES aes128(128);
	AES aes192(192);
	AES aes256(256);

	std::string plainText = "3243F6A8885A308D313198A2E0370734";
	std::string cipherKey = "2B7E151628AED2A6ABF7158809CF4F3C";

	std::cout << "Input : " << plainText << std::endl;
	std::string cipherText = aes128.encryption(plainText, cipherKey, true);
	std::cout << "Cipher : " << cipherText << std::endl;
	std::string origin = aes128.decryption(cipherText, cipherKey, true);
	std::cout << "origin : " << origin << std::endl;

	plainText = "014BAF2278A69D331D5180103643E99A";
	cipherKey = "E8E9EAEBEDEEEFF0F2F3F4F5F7F8F9FA";

	std::cout << "Input : " << plainText << std::endl;
	cipherText = aes128.encryption(plainText, cipherKey);
	std::cout << "Cipher : " << cipherText << std::endl;
	origin = aes128.decryption(cipherText, cipherKey);
	std::cout << "origin : " << origin << std::endl;

	plainText = "76777475F1F2F3F4F8F9E6E777707172";
	cipherKey = "04050607090A0B0C0E0F10111314151618191A1B1D1E1F20";

	std::cout << "\nInput : " << plainText << std::endl;
	cipherText = aes192.encryption(plainText, cipherKey);
	std::cout << "Cipher : " << cipherText << std::endl;
	origin = aes192.decryption(cipherText, cipherKey);
	std::cout << "origin : " << origin << std::endl;

	plainText = "069A007FC76A459F98BAF917FEDF9521";
	cipherKey = "08090A0B0D0E0F10121314151718191A1C1D1E1F21222324262728292B2C2D2E";

	std::cout << "\nInput : " << plainText << std::endl;
	cipherText = aes256.encryption(plainText, cipherKey);
	std::cout << "Cipher : " << cipherText << std::endl;
	origin = aes256.decryption(cipherText, cipherKey);
	std::cout << "origin : " << origin << std::endl;

	//aes128.setIV(time(NULL));
	std::cout << "\n AES/128/ECB mode\n";
	std::string str = "The current fire house installed within the building used by South Korea's agency for managing the industrial zone will move to the newly built three-story building, the official said.";
	std::string hexStr = aes128.convertStrToHexStr(str);
	cipherKey = "ABCDEFGHIJKLMNOP";
	cipherKey = aes128.convertStrToHexStr(cipherKey);
	std::cout << "Input(Str) : " << str << std::endl;
	std::cout << "Input(Hex) : " << hexStr << std::endl;
	cipherText = aes128.encryption(hexStr, cipherKey);
	std::cout << "Cipher : " << cipherText << std::endl;
	origin = aes128.decryption(cipherText, cipherKey);
	std::cout << "Output(Hex) : " << origin << std::endl;
	std::cout << "Output(Str) : " << aes128.convertHexStrToStr(origin) << std::endl;

	std::cout << "\n AES/128/CBC mode\n";
	AES aes128_CBC(128, MODE::CBC);
	std::cout << "Input(Str) : " << str << std::endl;
	std::cout << "Input(Hex) : " << hexStr << std::endl;
	cipherText = aes128_CBC.encryption(hexStr, cipherKey);
	std::cout << "Cipher : " << cipherText << std::endl;
	origin = aes128_CBC.decryption(cipherText, cipherKey);

	std::cout << "Output(Hex) : " << origin << std::endl;
	std::cout << "Output(Str) : " << aes128.convertHexStrToStr(origin) << std::endl;

	return 0;
}

 

올바르게 암호화/복호화 되는지 확인하려면, 아래 사이트에서 똑같이 입력해서 동일한 결과가 나오는지 확인할 수 있습니다. (ECB모드만 지원되는 것 같습니다. 검색해보면 다른 사이트도 많으니 참고바랍니다 !)

https://www.hanewin.net/encrypt/aes/aes-test.htm

 

AES (Rijndael) Encryption Test in JavaScript

AES (Rijndael) Encryption Test in JavaScript 2005 Herbert Hanewinkel [Description] [Test] The test vectors are from the AES supplied ones; more or less randomly taken from ecb_tbl.txt (I=42,81,14) Key (128):E8E9EAEBEDEEEFF0F2F3F4F5F7F8F9FA Plaintext:014BAF

www.hanewin.net


이렇게 AES 알고리즘에 대해서 알아보고, 직접 구현까지 해보았습니다.

최적화는 신경쓰지 않고 구현한 터라 부족한 점이 많지만, 시간이 된다면 최적화가 어떻게 진행되는지 알아보면 좋을 것 같습니다.

 

전체 코드는 아래 github에서 확인 가능합니다 !

https://github.com/junstar92/DataStructure_Algorithm/tree/master/Algorithm/AES

 

GitHub - junstar92/DataStructure_Algorithm

Contribute to junstar92/DataStructure_Algorithm development by creating an account on GitHub.

github.com

 

댓글