안 쓰던 블로그

AES 본문

CTF/Crypto

AES

proqk 2020. 9. 21. 00:37
반응형

현재까지 된 코드

 

AES_test.c
0.01MB

 

아래는 설명

AES는 DES를 대체할 목적으로 개최된 암호 표준 공모에서 채택된 암호 표준이다

 

확장성을 고려하여 설계되어서 키 길이는 128/192/256 bit를 가지고 키 길이에 상관없이 블록 길이는 128 bit이며 라운드 수는 10/12/14를 가진다.

 

대략 적인 순서는

 

1. 라운드키와 xor한다. (AddRoundKey)

2. 바이트를 치환한다. (SubBytes)

3. 행별로 바이트를 옮긴다. (ShiftRows)

4. 열 별로 바이트를 섞는다. (4)

5. 라운키와 xor한다. (AddRoundKey)

6. (라운드 수 - 1) 만큼 2~5를 반복한다.

7. 마지막 라운드는 MixColumns를 제외하고 수행한다.

 

복호화는 각 단계의 역연산을 거꾸로 수행하면 된다.


1. AddRoundKey

void AddRoundKey(unsigned char state[16], const unsigned char roundKey[16]) //라운드키와 xor
{
	unsigned char i; //uint8_t
	for (i = 0; i < 16; i++) {
		state[i] ^= roundKey[i];
	}
}

라운드 키와 xor한다

 

2. SubBytes

void SubBytes(unsigned char state[16]) //바이트 치환
{
	unsigned char i;
	for (i = 0; i < 16; i++) {
		state[i] = SBox[state[i]];
	}
}

이 행렬식을 곱하여 바이트를 치환하는데, 메모리가 부족한 시스템이라면 직접 계산하지만 보통은 미리 다 계산되어 있는 치환표 SBox를 이용한다

 

예를 들어 만약 37을 바꾼다면 b2가 된다

 

3. ShiftRows

void ShiftRows(unsigned char state[16])
{
	//첫행은 움직이지 않고 둘째행은 1번, 셋째행은 2번, 넷째행은 3번 바이트단위로 왼쪽순환쉬프트
	unsigned char tmp = state[1];
	state[1] = state[5];
	state[5] = state[9];
	state[9] = state[13];
	state[13] = tmp;

	unsigned char tmp1 = state[2], tmp2 = state[6];
	state[2] = state[10];
	state[6] = state[14];
	state[10] = tmp1;
	state[14] = tmp2;

	unsigned char tmp3 = state[3], tmp4 = state[7], tmp5 = state[11];
	state[3] = state[15];
	state[7] = tmp3;
	state[11] = tmp4;
	state[15] = tmp5;
}

첫 행은 움직이지 않고 둘째행은 왼쪽으로 1번, 셋째행은 왼쪽으로 2번, 넷째행은 3번 바이트 단위로 이동하는 왼쪽 순환 쉬프트 과정을 거친다

 

4. MixColumns

#define xtime(x) ((x<<1) ^ (((x>>7) & 1) * 0x1b))

void MixColumns(unsigned char state[16]) //열 별로 xor
{
	int i;
	unsigned char Tmp, Tm, t;
	for (i = 0; i < 4; i++)
	{
		t = state[i];
		Tmp = state[i] ^ state[i+4] ^ state[i + 8] ^ state[i + 12];
		
		Tm = state[i] ^ state[i + 4];
		Tm = xtime(Tm);
		state[i] ^= Tm ^ Tmp;

		Tm = state[i + 4] ^ state[i + 8];
		Tm = xtime(Tm);
		state[i + 4] ^= Tm ^ Tmp;

		Tm = state[i + 8] ^ state[i + 12];
		Tm = xtime(Tm);
		state[i + 8] ^= Tm ^ Tmp;

		Tm = state[i + 12] ^ t;
		Tm = xtime(Tm);
		state[i + 12] ^= Tm ^ Tmp;
	}
}

이전 라운드에서 나온 값의 각 열에 대해 다음 행렬을 연산한다

그림에서 나온 예시는 d4 * 2 + bf*3 + 5d*1 + 30*1 이다. 곱은 그대로 2, 3, 1 등을 곱하면 된다

근데 한 성분의 비트 수가 8비트밖에 되지 않아서 2나 3을 곱하다 보면 오버플로우가 나게 된다

오버플로우가 발생하면 011b와 XOR연산을 해 준다

각 성분마다 곱셈이 끝나면 더해줘야 하는데, 여기서 하는 덧셈은 실제 덧셈이 아니라 XOR로 더해 주어야 한다

 

근데 이 코드로 돌리면 제대로 연산 되지 않고 다른 값이 나온다. tiny_aes랑 다른 점이 없어 보이는데..

 

5. AddRoundKey

암호문과 평문을 XOR한다

여기까지 하면 한 라운드가 종료된다


키 확장

라운드 키는 128비트 기준 총 11개(첫 Addroundkey포함)이고 각 키는 4개의 의 워드로 구성되기때문에 총 44개의 워드가 필요하다.

 

키 확장 순서

1. 키를 4바이트씩 나누어 4개의 워드로 만든다.

2. 이전 번의 워드와 4번째전 워드를 xor하여 새 워드를 생성한다. ( 4의 배수 번째의 경우 이전 워드를 g함수에 통과시킨다.)

    2-1. 입력의 워드를 1바이트만큼 왼쪽 순환시프트를 한다.

    2-2. Subbytes에 썼던 치환을 여기서도 사용한다.

    2-3. 상수값 R과 xor하여 결과를 반환한다.

3. 워드가 44개 생성될때까지 2를 반복한다.

 

R상수값

const unsigned char roundConstant[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };

 

 

void KeyExpansion(unsigned char roundKey[11][32], const unsigned char masterKey[16], int r) //키 확장
{
	unsigned char i, j, k;
	unsigned char temp[4]; //WORD
	unsigned char Nk = 4, Nb = 4, Nr = 10; //키 바이트수, 블럭 수, 라운드 수
	for (i = 0; i < Nk; i++) { //키를 4바이트씩 나누어 4개 워드로 만든다
		roundKey[r][(i * 4) + 0] = masterKey[(i * 4) + 0];
		roundKey[r][(i * 4) + 1] = masterKey[(i * 4) + 1];
		roundKey[r][(i * 4) + 2] = masterKey[(i * 4) + 2];
		roundKey[r][(i * 4) + 3] = masterKey[(i * 4) + 3];
	}

	for (i = Nk; i < Nb * (Nr + 1); ++i) {
		//워드가 44개 나올 때까지 이전 번 워드와 4번째 전 워드를 xor하여 새 워드 생성
		k = (i - 1) * 4;
		temp[0] = roundKey[k + 0];
		temp[1] = roundKey[k + 1];
		temp[2] = roundKey[k + 2];
		temp[3] = roundKey[k + 3];

		if (i % Nk == 0) {
			unsigned char u8tmp = temp[0]; //입력 워드 1바이트 왼쪽 순환 시프트
			temp[0] = temp[1];
			temp[1] = temp[2];
			temp[2] = temp[3];
			temp[3] = u8tmp;

			temp[0] = SBox[temp[0]]; //Subbytes 치환 사용
			temp[1] = SBox[temp[1]];
			temp[2] = SBox[temp[2]];
			temp[3] = SBox[temp[3]];

			temp[0] = temp[0] ^ roundConstant[i / Nk]; //상수 r과 xor
		}
		else if ((i % Nk == 4)) { //4배수면 g통과
			temp[0] = SBox[temp[0]];
			temp[1] = SBox[temp[1]];
			temp[2] = SBox[temp[2]];
			temp[3] = SBox[temp[3]];
		}
		j = i * 4;
		k = (i - Nk) * 4;
		roundKey[r][j + 0] = roundKey[r][k + 0] ^ temp[0]; 
		roundKey[r][j + 1] = roundKey[r][k + 1] ^ temp[1];
		roundKey[r][j + 2] = roundKey[r][k + 2] ^ temp[2];
		roundKey[r][j + 3] = roundKey[r][k + 3] ^ temp[3];
	}
}

이 부분도 다른 값이 나오는데.. 해결하고 있어요

 


AES 실행

void AES(unsigned char cipherText[16], const unsigned char plainText[16], const unsigned char key[16])
{
	unsigned char state[16] = { 0, };
	for (int i = 0; i < 16; i++)
		state[i] = plainText[i];

	AddRoundKey(state, key);

	int Nr = 3;

	for (int round = 1; round < Nr; round++)
	{
		SubBytes(state);
		ShiftRows(state);
		MixColumns(state);
		AddRoundKey(state, key);
	}

	SubBytes(state);
	ShiftRows(state);
	AddRoundKey(state, key);
}

 

반응형
Comments