티스토리 뷰

출처 : cryptocontest.kr/

 

Home - SEC연구소 | 암호분석경진대회

 

cryptocontest.kr

2020 암호경진대회 2번 문제입니다. 최적화를 통해 해당 암호화 알고리즘을 누가 더 빠르게 동작하도록 만드느냐하는 문제였습니다. 

 

문제에서 요구하는 수정해야될 key_gen과 enc 함수는 다음과 같습니다.

void key_gen(u8 *rnd, u8 *key) {
	u8 key_in[2];
	u8 tmp1, tmp2;
	u16 *key_p;
	u16 con = 0x9ABC;

	key_in[0] = key[0];
	key_in[1] = key[1];

	key_p = (u16*) key_in;

	int i;
	for (i = 0; i < ROUND_NUM; i++) {

		if (i % 2 == 0) {
			key_in[0] = ROL8(key_in[0], 1) + ROL8(key_in[0], 5);
			key_in[1] = ROL8(key_in[1], 3) + ROL8(key_in[1], 7);
	} else {
	*key_p = ROL16(*key_p, 1) + ROL16(*key_p, 9) + ROL16(con, (i%16));
 	}

	tmp1 = key_in[0] + key_in[1];
	tmp2 = key_in[0] ^ key_in[1];

	key_in[0] = tmp1;
	key_in[1] = tmp2;

	rnd[i * 2 + 0] = key_in[0];
	rnd[i * 2 + 1] = key_in[1];
	}
}
void enc(u8 *text, u8 *rnd) {
	u8 text_in[2];
    
	u16 *text_p;
    
	text_in[0] = text[0];
	text_in[1] = text[1];
    
	text_p = (u16*) text_in;
    
	int i;
	for (i = 0; i < ROUND_NUM; i++) {
		if (i % 2 == 0) {
			*text_p = ROL16(*text_p, 4);
		} else {
			*text_p = ROL16(*text_p, 8);
		}
		text_in[0] = text_in[0] + rnd[i * 2 + 0];
		text_in[1] = text_in[1] ^ rnd[i * 2 + 1];
	}
    
	text[0] = text_in[0];
	text[1] = text_in[1];
}

 

평가 방법은 다음과 같습니다.

1. 테스트 벡터 통과

문제에 총 3가지의 테스트 벡터가 있습니다. 이를 다 통과해야합니다.

(물론 모든 경우에 대해 다 통과해야 완벽한 암호가 됩니다.)

2. 문서화

답안을 얼마나 깔끔하게 썼나 그런걸 보는 것 같습니다.

3. 벤치마크 결과 (상대평가)

사실 제일 중요한 부분입니다. 기본으로 주어진 두 함수를 최적화를 제대로 하였다면 테스트 벡터를 통과할 것이고 문서도 적당히 답안을 쓰면 됩니다. 벤치마크 결과는 최적화를 얼마나 하였느냐 하는 건데 결과적으로 저희 팀은 801ms의 결과를 얻었었습니다. 

(1등 팀은 얼핏 듣기로 100ms 정도였습니다.)

 

제출 마감 1주일 전부터는 2번만 잡고 있었던 것 같습니다. (4번 부채널분석 문제는 이해하는데 어려웠고, 5번 해시함수 문제는 끝없는 문제이기에 제가 4번을 끝낸 뒤 2번을 맡고, 다른 팀원이 5번을 끝까지 머리를 굴렸던 기억이 나네요!)

 

저희 팀에는 어셈블리어를 할 줄 아는 학생이 없어 그냥 C코드로 제출하였는데 저희보다 최적화를 더 잘한 팀들의 얘기를 들어보니 어셈블리어를 활용했었습니다. 나중에 여유가 되면 공부를 해봐야할 것 같습니다.

 

아래 설명의 벤치마크 결과는 구현 방법에 따라 차이가 발생할 수 있고 더 좋은 결과를 얻으신다면 축하드립니다!

 

문제 풀이

우선 한가지 짚고 넘어가야할 점은 이 문제는 아두이노를 활용하는 것입니다. 아두이노-uno버전을 이용하여 모든 팀이 동일한 조건으로 최적화를 진행할 수 있습니다. 그리고 주어진 코드를 그대로 진행하면 3909ms의 벤치마크 결과를 얻습니다.

 

아두이노는 8bit 프로세서를 가집니다. 일반 컴퓨터가 32, 64bit 프로세서임을 감안하면 평소 알고리즘 문제를 풀거나 코딩을 할 때와는 다른 결과를 얻을 수도 있습니다. 

 

우선 문제에 주어진 ROL 함수는 8, 16bit x를 n bit만큼 로테이션시킨 것입니다. 

n bit만큼 << 로 옮긴후 밀린 값을 뒤에 padding 하는 것이라 생각하면 됩니다.

 

1. key_gen

key_gen 함수부터 살펴보면, 8bit key[2] 배열을 입력 받아 두 배열 값을 연결(?)한 16bit 값을 key_p 값으로 가집니다.

그리고 ROUND_NUM 만큼의 루프로 짝수번째는 key[0] 값을 1,5bit 로테이션, key[1]값을 3, 7bit 로테이션합니다.

그리고 홀수번째는 key_p값을 1,9 bit 로테이션한 값과 con 값을 (i%16)만큼 로테이션 한 값을 모두 더합니다.

그리고 난 뒤 key[0], key[1] 값을 각각 +, ^ 연산을 이용해 새로운 값을 얻어 각각 rnd에 저장하는 형태입니다.

 

적다보니 key_in에 대한 내용을 말하지 않았는데, 그건 바로 key_in에 따로 저장하는 연산이 굳이 필요없기 때문입니다. key를 인자에서 계속 불러와 사용하거나 지역 변수 key_in을 이용해 사용하느냐 차이인데 처음에는 인자로 받은 값을 계속해서 불러오는 것이 비효율적이라 생각하였지만 실험결과 같은 결과를 얻었기에 생략하도록 했습니다.

 

또한 tmp 변수 또한 필요가 없습니다. tmp값에 저장된 값을 다시 불러와 rnd에 넣는 것이므로 rnd 자체를 tmp처럼 사용할 수 있습니다.

tmp1 -> rnd[2i+0], tmp2 -> rnd[2i+1] 로 이용할 수 있습니다. rnd 인자에서 계속해서 불러오는게 비효율적일수 있으나 기본적으로 rnd={ 0, }이기 때문에 tmp값을 없애는 것이 더 효율적이라는 것을 실험 결과 알 수 있었습니다.

 

여기까지는 사실 최적화 과정에서 크게 영향을 끼치지 않는 요소입니다. 결과적으로 최적화에 중요한 부분을 적용하면 위 key_in, tmp를 이용한 것은 최적화에 그리 영향을 끼치지 않았습니다. 굳이 적용하지 않아도 되는 부분입니다.

(위 부분을 적용하면 3909ms -> 3825ms가 됩니다.)

 

이제부터 최적화에 중요한 내용을 살펴보겠습니다.

 

우선 for문이 제일 거슬립니다. for문은 loop를 돌며 i<ROUND_NUM 을 계속 체크하게 됩니다. 그렇기 때문에 for문을 싸그리 풀어주도록 합니다. 하지만 여기 함정이 있습니다. 바로 아두이노가 8bit 프로세서라는 점입니다.

아두이노를 이번에 처음 다뤄보아 정확한 이유는 알지 못하지만 for문을 전부 풀었을 때 시간이 줄긴 하였지만, 각각 8라운드, 16라운드, 32라운드, 64라운드로 묶었을 때 결과가 모두 달랐고, enc 내부의 for문을 8라운드, 16라운드, 32라운드, 64라운드 중 어떤 것을 잡느냐에 따라서도 결과가 달랐습니다.

(기존에 주어진 것에서 if문을 푼 것을 1라운드로 잡았다.)

for문을 모두 푼 것과 풀지 않은 것까지 포함하여 총 64가지의 경우를 모두 돌려본 결과, key_gen과 enc 모두 16라운드로 묶었을 때가 가장 속도가 빨랐습니다. 

for문 내부에서 추가적으로 구할 수 있는 것은 ROL(con,(i%16)) 값을 미리 계산할 수 있습니다. 아두이노는 아주 작은 메모리를 가집니다.

 

아두이노 우노의 스펙은 아래와 같습니다.

SRAM이라고 하는 기본 메모리(하드디스크 역할)가 2KB 밖에 안 되고, EEPROM이라는 읽기 전용 메모리 또한 1KB 밖에 되지 않습니다. 그렇기 때문에 미리 table을 구현할 때 메모리를 신경써야합니다. 

(아두이노 IDE로 돌리면 메모리 얼마나 남는지 계산해줍니다. 하지만 캐시 저장 등 여분 메모리를 위해 80%~90%를 넘을 수 없습니다.)

 

하지만 ROL16(con, i%16) 값의 table을 미리 계산하기에는 넉넉하므로 미리 통째로 계산해줍니다. (16*16=256bit=32byte) 그럼으로써 이 계산 부분을 없앨 수 있습니다. 이 과정을 모두 적용하면 1510ms로 줄어듭니다.

(enc도 16라운드 씩 묶었을 때)

 

추가적으로 ROL8 부분도 table을 이용하여 미리 계산할 수 있습니다. 8bit 결과를 256가지에 대해 미리 계산하는 table을 2개 만들어야 합니다. (1,5 bit 로테이션, 3,7 bit 로테이션) 이를 이용해 불필요한 계산을 줄일 수 있습니다.

이 과정을 적용할 시 1283ms로 줄어듭니다. (enc도 추가적으로 할 계산이 있지만 밑에서 설명하겠습니다.)

 

문제는 그 다음부터 발생합니다. ROL16을 이용한 경우는 모든 경우에 대해 table을 만드려면 메모리가 초과합니다. EEPROM을 이용해 어떻게든 메모리를 끌어다 쓸려했지만 EEPROM에 저장한 값을 불러오는데 더 많은 시간이 든다는 것을 알게 되었습니다. 그렇기에 ROL16 부분은 다른 방법을 이용해봅시다.

 

저희는 1,9 bit를 로테이션 한 것에 아이디어를 얻었습니다. ROL16(*key_p, 9)는 ROL16(*key_p, 1) 결과를 key[0], key[1]부분을 swap 한 것과 같습니다. 

그러므로 이 과정에서 ROL16(*key_p,1) 또는 ROL16(*key_p,9) 중 하나만 계산하면 됩니다. (둘 다 결과 적으로 비트를 옮긴 횟수는 동일하고 실험결과 좌,우로 비트 옮기는 것은 동일한 시간을 측정하였으므로 둘 중 아무거나 계산하면 됩니다.)

swap 값을 어떻게 계산할 것인지도 관건인데 저는 포인터를 이용하여 새로운 함수를 하나 만들었습니다.

u16 swap_arr(u8* arr) {                 
	u8 arr2[2] = { 0,0 };
	arr2[0] = arr[1];
	arr2[1] = arr[0];
	return *(u16*)arr2;
}

여러 방법을 시도해보았지만 이 경우가 가장 결과가 좋았기에 이 함수를 사용했습니다.

아두이노가 8bit 프로세서이기 때문에 생각처럼 잘 되지 않았습니다. 여기서 더 좋은 방법이 있다면 그 방법을 사용하심이 좋을 것 같습니다.

 

이 과정까지 거치면 1068ms로 줄어듭니다. (enc 함수 또한 수정한 결과)

취약점 분석 과정이 남았지만 enc 에 대해 설명 후 취약점 분석으로 마무리하겠습니다.

 

2. enc

enc 함수는 key_gen보다는 단순해보입니다.

key_gen에서와 마찬가지로 8bit 배열 text[2]를 인자로 받아 text_p라는 16bit 값으로 저장 후 ROUND_NUM 만큼의 loop를 돌며 짝수 번째는 4bit 로테이션, 홀수 번째는 8bit 로테이션을 합니다. 그리고 key_gen에서 구한 rnd를 이용하여 각각 +, ^ 연산을 합니다.

 

enc의 경우 key_gen보다는 최적화를 할 요소를 크게 찾지 못하였습니다. text_in 같은 경우도 최적화를 마무리하는데 크게 작용하는 요소는 아니고 key_gen에서 언급한 실험 과정에서 enc 내부 또한 16라운드로 묶었습니다. 

 

enc 함수에는 ROL16 밖에 쓰이지 않았기 때문에 table을 따로 구현할 것도 없고 유일한 것은 ROL16(*text_p,8)의 결과는 text[0], text[1]을 swap한 것입니다.

rnd도 key_gen으로 계산하는 것이기 때문에 +,^ 연산은 기본 연산이라 따로 건드릴 것도 없어 보입니다.

한 가지 걸리는 점은 ROL16(*text_p,4)인데 이 부분은 해결하지 못하였습니다. 4bit 로테이션으로 얻은 결과와 같은 값을 가지기 위해서는 앞서 했던 swap 등의 방법을 사용하기에 무리가 있습니다.

(nibble이라는 4bit 단위가 있지만 이론상 쓰이는 단위에 불과합니다.)

이 부분을 최적화한다면 좀 더 좋은 결과가 있었겠지만 아쉽게도 해결하지 못했습니다. 여기서 좋은 아이디어를 알아내신다면 그 방법으로 하시면 됩니다.

 

3. 취약점 공격

마지막으로 이 암호의 취약점에 대해 알아보겠습니다. 최적화에만 신경 쓰다보니 취약점에 대해 알아차린 건 제출 3일 전 쯤이었습니다. 계속 되도 안 되는 ROL16(*text_p,4)를 붙잡고 있었으면 큰일날 뻔 했습니다.

 

우선 rnd값을 계산함에 있어서 모든 rnd를 직접 다 출력해본 결과, 반복되는 구간이 있습니다. 이 부분이 취약점입니다. rnd를 이용하여 text를 변환하지만, rnd가 반복되는 구간이 있기에 이 부분을 이용하여 취약점 공격을 할 수 있었습니다.

 

key={0x9A, 0xBD} 일 때

rnd[64]부터 살펴보면 64~95, 96~127, ..., 224~255 구간이 반복됨을 알 수 있습니다. 모든 key {0x00,0x00}~{0xFF,0xFF}에 대해 계산한 결과 키 마다 반복되는 전체 구간은 다르지만 64~95, 96~127, ..., 224~255로 묶었을 때 이 부분이 공통적으로 반복이 됨을 알 수 있습니다. 이를 이용하여 key_gen 내부의 loop를 rnd[95]까지만 구하게 만들면 됩니다. 

 

이를 이용하면 801ms까지 줄어듭니다.

 

 

전체적으로 살펴보면 다음과 같습니다.

순서

key_gen

enc

결과(ms)

1

key_in 제거 (key_in 대신 key 사용)

text_in 제거 (text_in 대신 text 사용)

3825

2

tmp1, tmp2 제거 (rnd를 이용하여 계산)

*

3825

3

if문 사용하지 않고,

기존 라운드 16개를 for문의 한 라운드로 묶음,

ROL16(con, (i%16)) 미리 계산

if문 사용하지 않고,

기존 라운드 16개를 for문의 한 라운드로 묶음

1510

4

ROL8 계산 부분은 table 활용

(key_table1 : ROL8(key[0],1)+ROL8(key[0],5)

(key_table2 : ROL8(key[1],3)+ROL8(key[1],7)

ROL16(*text_p,8)을 계산하지 않고 rnd와의 연산 순서를 조정하여 같은 결과를 얻도록 수정

1283

5

ROL16(*key_p,9)ROL16(*key_p,1) 계산 후 key[0], key[1]swap한 것임을 이용

*

1068

6

ROL16(*key_p,1) 계산 시 함수 호출 대신

직접 적용

ROL16(*text_p,4) 계산 시 함수 호출 대신

직접 적용

1068

7

모든 key ({0x00, 0x00} ~ {0xFF, 0xFF})에 대해

rnd[64]~rnd[95] = rnd[96]~rnd[127] = ....

= rnd[224]~rnd[255]를 만족함을 이용

*

801

 

최종 결과

주요 부분 소스코드

u8 key_table1[256]={
  0x00,0x22,0x44,0x66,0x88,0xaa,0xcc,0xee,0x11,0x33,0x55,0x77,0x99,0xbb,0xdd,0xff,0x22,0x44,0x66,0x88,0xaa,0xcc,0xee,0x10,
  0x33,0x55,0x77,0x99,0xbb,0xdd,0xff,0x21,0x44,0x66,0x88,0xaa,0xcc,0xee,0x10,0x32,0x55,0x77,0x99,0xbb,0xdd,0xff,0x21,0x43,
  0x66,0x88,0xaa,0xcc,0xee,0x10,0x32,0x54,0x77,0x99,0xbb,0xdd,0xff,0x21,0x43,0x65,0x88,0xaa,0xcc,0xee,0x10,0x32,0x54,0x76,
  0x99,0xbb,0xdd,0xff,0x21,0x43,0x65,0x87,0xaa,0xcc,0xee,0x10,0x32,0x54,0x76,0x98,0xbb,0xdd,0xff,0x21,0x43,0x65,0x87,0xa9,
  0xcc,0xee,0x10,0x32,0x54,0x76,0x98,0xba,0xdd,0xff,0x21,0x43,0x65,0x87,0xa9,0xcb,0xee,0x10,0x32,0x54,0x76,0x98,0xba,0xdc,
  0xff,0x21,0x43,0x65,0x87,0xa9,0xcb,0xed,0x11,0x33,0x55,0x77,0x99,0xbb,0xdd,0xff,0x22,0x44,0x66,0x88,0xaa,0xcc,0xee,0x10,
  0x33,0x55,0x77,0x99,0xbb,0xdd,0xff,0x21,0x44,0x66,0x88,0xaa,0xcc,0xee,0x10,0x32,0x55,0x77,0x99,0xbb,0xdd,0xff,0x21,0x43,
  0x66,0x88,0xaa,0xcc,0xee,0x10,0x32,0x54,0x77,0x99,0xbb,0xdd,0xff,0x21,0x43,0x65,0x88,0xaa,0xcc,0xee,0x10,0x32,0x54,0x76,
  0x99,0xbb,0xdd,0xff,0x21,0x43,0x65,0x87,0xaa,0xcc,0xee,0x10,0x32,0x54,0x76,0x98,0xbb,0xdd,0xff,0x21,0x43,0x65,0x87,0xa9,
  0xcc,0xee,0x10,0x32,0x54,0x76,0x98,0xba,0xdd,0xff,0x21,0x43,0x65,0x87,0xa9,0xcb,0xee,0x10,0x32,0x54,0x76,0x98,0xba,0xdc,
  0xff,0x21,0x43,0x65,0x87,0xa9,0xcb,0xed,0x10,0x32,0x54,0x76,0x98,0xba,0xdc,0xfe}; // key_table1[u8 x] = ROL8( u8 x, 1)+ROL8( u8 x , 5)

u8 key_table2[256]{
  0x00,0x88,0x11,0x99,0x22,0xaa,0x33,0xbb,0x44,0xcc,0x55,0xdd,0x66,0xee,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,
  0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x11,0x99,0x22,0xaa,0x33,0xbb,0x44,0xcc,0x55,0xdd,0x66,0xee,0x77,0xff,0x88,0x10,
  0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x10,0x98,0x22,0xaa,0x33,0xbb,0x44,0xcc,0x55,0xdd,
  0x66,0xee,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x10,0x98,0x21,0xa9,
  0x33,0xbb,0x44,0xcc,0x55,0xdd,0x66,0xee,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,
  0xff,0x87,0x10,0x98,0x21,0xa9,0x32,0xba,0x44,0xcc,0x55,0xdd,0x66,0xee,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,
  0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x10,0x98,0x21,0xa9,0x32,0xba,0x43,0xcb,0x55,0xdd,0x66,0xee,0x77,0xff,0x88,0x10,
  0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x10,0x98,0x21,0xa9,0x32,0xba,0x43,0xcb,0x54,0xdc,
  0x66,0xee,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,0xff,0x87,0x10,0x98,0x21,0xa9,
  0x32,0xba,0x43,0xcb,0x54,0xdc,0x65,0xed,0x77,0xff,0x88,0x10,0x99,0x21,0xaa,0x32,0xbb,0x43,0xcc,0x54,0xdd,0x65,0xee,0x76,
  0xff,0x87,0x10,0x98,0x21,0xa9,0x32,0xba,0x43,0xcb,0x54,0xdc,0x65,0xed,0x76,0xfe}; // key_table2[u8 x] = ROL8( u8 x, 3)+ROL8( u8 x , 7)

u16 swap_arr(u8* arr) {                  // swap_arr : 입력 인자 arr의 arr[0], arr[1]을 swap하여 16비트 결과를 출력
  u8 arr2[2] = { 0,0 };
  arr2[0] = arr[1];
  arr2[1] = arr[0];
  return *(u16*)arr2;
} 

void key_gen(u8 *rnd, u8 *key){
  u16 *key_p;     
  key_p=(u16*) key;                      // key_in 대신 입력 인자 key 사용
  
  int i,j;                               
  for(i=0;i<3;i++){                      // for문 : 기존의 16개의 라운드를 for문의 한 라운드로 사용
    j=i*32;
    
    key[0]=key_table1[key[0]];           // ROL8(key[0],1)+ROL8(key[0],5)와 같은 결과
    key[1]=key_table2[key[1]];           // ROL8(key[1],3)+ROL8(key[1],7)와 같은 결과
    rnd[j+0]=key[0]+key[1];              // tmp 대신 입력 인자 rnd 활용
    rnd[j+1]=key[0]^key[1];
    key[0]=rnd[j+0];
    key[1]=rnd[j+1];

    *key_p=(*key_p<<1)|(*key_p>>15);     // ROL16(*key_p,1)을 풀어쓴 결과
    *key_p=*key_p+swap_arr(key)+0x3579;  // ROL16(*key_p,9)는 ROL16(*key_p,1) 계산 후 key[0], key[1]을 swap한 것과 같음. ROL16(con,(i%16)) 미리 계산
    rnd[j+2]=key[0]+key[1];
    rnd[j+3]=key[0]^key[1];
    key[0]=rnd[j+2];
    key[1]=rnd[j+3];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+4]=key[0]+key[1];
    rnd[j+5]=key[0]^key[1];
    key[0]=rnd[j+4];
    key[1]=rnd[j+5];

    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0xD5E4;
    rnd[j+6]=key[0]+key[1];
    rnd[j+7]=key[0]^key[1];
    key[0]=rnd[j+6];
    key[1]=rnd[j+7];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+8]=key[0]+key[1];
    rnd[j+9]=key[0]^key[1];
    key[0]=rnd[j+8];
    key[1]=rnd[j+9];

    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0x5793;
    rnd[j+10]=key[0]+key[1];
    rnd[j+11]=key[0]^key[1];
    key[0]=rnd[j+10];
    key[1]=rnd[j+11];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+12]=key[0]+key[1];
    rnd[j+13]=key[0]^key[1];
    key[0]=rnd[j+12];
    key[1]=rnd[j+13];

    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0x5E4D;
    rnd[j+14]=key[0]+key[1];
    rnd[j+15]=key[0]^key[1];
    key[0]=rnd[j+14];
    key[1]=rnd[j+15];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+16]=key[0]+key[1];
    rnd[j+17]=key[0]^key[1];
    key[0]=rnd[j+16];
    key[1]=rnd[j+17];
    
    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0x7935;
    rnd[j+18]=key[0]+key[1];
    rnd[j+19]=key[0]^key[1];
    key[0]=rnd[j+18];
    key[1]=rnd[j+19];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+20]=key[0]+key[1];
    rnd[j+21]=key[0]^key[1];
    key[0]=rnd[j+20];
    key[1]=rnd[j+21];
    
    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0xE4D5;
    rnd[j+22]=key[0]+key[1];
    rnd[j+23]=key[0]^key[1];
    key[0]=rnd[j+22];
    key[1]=rnd[j+23];
    
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+24]=key[0]+key[1];
    rnd[j+25]=key[0]^key[1];
    key[0]=rnd[j+24];
    key[1]=rnd[j+25];
    
    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0x9357;
    rnd[j+26]=key[0]+key[1];
    rnd[j+27]=key[0]^key[1];
    key[0]=rnd[j+26];
    key[1]=rnd[j+27];
  
    key[0]=key_table1[key[0]];
    key[1]=key_table2[key[1]];
    rnd[j+28]=key[0]+key[1];
    rnd[j+29]=key[0]^key[1];
    key[0]=rnd[j+28];
    key[1]=rnd[j+29];
    
    *key_p=(*key_p<<1)|(*key_p>>15);
    *key_p=*key_p+swap_arr(key)+0x4D5E;
    rnd[j+30]=key[0]+key[1];
    rnd[j+31]=key[0]^key[1];
    key[0]=rnd[j+30];
    key[1]=rnd[j+31];
  }
  for(int s=0;s<8;s++)            // rnd가 반복됨을 이용. (rnd[64]~rnd[95])의 값을 (rnd[96]~rnd[127]),...,(rnd[224]~rnd[255])에 복사하였다.
  {
    int t=4*s;
    u32 rnd_copy=*((u32*)(rnd+64+t));
    *((u32*)(rnd+96+t))=rnd_copy;
    *((u32*)(rnd+128+t))=rnd_copy;
    *((u32*)(rnd+160+t))=rnd_copy;
    *((u32*)(rnd+192+t))=rnd_copy;
    *((u32*)(rnd+224+t))=rnd_copy;
  }
}
void enc(u8 *text, u8 *rnd){
  u16 *text_p;
  text_p=(u16*) text;                      // text_in 대신 text 사용
  
  int i,j;
  for(i=0;i<8;i++){                        // for문 : 기존의 라운드 16개를 for문의 한 라운드로 사용
    j=i*32;
    *text_p=(*text_p<<4)|(*text_p>>12);    // ROL16(*text_p,4)를 풀어쓴 결과
    text[0]=(text[0]+rnd[j+0])^rnd[j+3];   // ROL16(*text_p,8)을 계산하지 않고, rnd와의 연산 순서를 변경하여 같은 결과 나오게끔 연산 순서 조정
    text[1]=(text[1]^rnd[j+1])+rnd[j+2];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]^rnd[j+5])+rnd[j+6];
    text[1]=(text[1]+rnd[j+4])^rnd[j+7];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]+rnd[j+8])^rnd[j+11];
    text[1]=(text[1]^rnd[j+9])+rnd[j+10];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]^rnd[j+13])+rnd[j+14];
    text[1]=(text[1]+rnd[j+12])^rnd[j+15];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]+rnd[j+16])^rnd[j+19];
    text[1]=(text[1]^rnd[j+17])+rnd[j+18];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]^rnd[j+21])+rnd[j+22];
    text[1]=(text[1]+rnd[j+20])^rnd[j+23];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]+rnd[j+24])^rnd[j+27];
    text[1]=(text[1]^rnd[j+25])+rnd[j+26];
    
    *text_p=(*text_p<<4)|(*text_p>>12);
    text[0]=(text[0]^rnd[j+29])+rnd[j+30];
    text[1]=(text[1]+rnd[j+28])^rnd[j+31];
  }
}

 

아두이노라는 장비를 처음 써봤고, 어셈블리어도 할 줄 몰랐기에 그닥 좋은 결과는 아닌 것 같습니다. 이 자료를 바탕으로 더 좋은 결과를 얻으시길 바랍니다!

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함