※ 본 프로젝트는 offensive research가 아닌 defensive 관점에서 진행한 학술적인 연구입니다.
✉ 윤형준 encrypt@kakao.com
[목차]
Overview
NEAT 암호 알고리즘 분석 프로젝트의 마지막 파트가 될 것 같다.
암호분석(cryptanalysis)을 안 할 수도 있고 혹시 하더라도 포스팅을 하지 않을 수도 있다.
앞서 분석한 NEAT를 C와 Python으로 구현하고 github에 공개한다.
Optimization
InvMul
modular 에서 을 구하는 문제를 fertmat's little theorem
을 이용해 제곱을 빠르게 구하는 문제로 치환하는 방법이다.
modular 에서 의 역원을 구해야 한다.
소수 라고 하자. 이때 는 인 fermat number다. ()
fermat's little theorem을 이용해 아래와 같이 전개할 수 있다.
하드웨어를 이용한다면 와 같은 방법이 효율적이겠다.
파이썬 코드에서 벤치마킹한 결과 Left-to-right 5-ary exponentiation
을 이용해 squaring을 빠르게 수행하는 pow
함수가 가장 빨랐다.
Involution
리버싱한 NEAT 구현체에서는 Encrypt() 함수와 Decrypt() 함수가 따로 구현되어 있었다.
Theori에서는 flag 값에 따른 분기문을 이용했다.
본인은 DecKeySchdule 함수에서 dec round xcon을 모두 swap하여 암호화와 복호화를 한 함수로 구현하였다.
Implementation
M4KE_NE4T_NE4TER
(NEAT를 더 깔끔하게!)라는 평문을 암호화/복호화 하는 코드다.
NEAT - Python
neat.py
#!/usr/bin/python3 # -*- coding: utf-8 -*- INT_BITS = 16 INT_SIZE = 0xFFFF key_const = [0xFD56, 0xC7D1, 0xE36C, 0xA2DC] fixed_xcon = [0, 1, 0, 1, 2, 3, 2, 3] xbox = [ 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 3, 2, 0, 1, 1, 0, 2, 3, 2, 3, 0, 1, 1, 0, 2, 3, 3, 2, 0, 1, 0, 2, 1, 3, 3, 2, 0, 1, 2, 0, 1, 3, 2, 3, 0, 1, 2, 0, 1, 3, 3, 2, 0, 1, 0, 3, 1, 2, 2, 3, 0, 1, 0, 3, 1, 2, 3, 2, 0, 1, 3, 0, 1, 2, 2, 3, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 1, 2, 0, 3, 2, 3, 0, 1, 1, 2, 0, 3, 3, 2, 0, 1, 2, 1, 0, 3, 2, 3, 0, 1, 2, 1, 0, 3, 3, 2, 0, 1, 1, 3, 0, 2, 2, 3, 0, 1, 1, 3, 0, 2, 3, 2, 0, 1, 3, 1, 0, 2, 2, 3, 0, 1, 3, 1, 0, 2, 3, 2, 0, 1, 2, 3, 0, 1, 3, 2, 0, 1, 3, 2, 0, 1, 2, 3, 0, 2, 0, 1, 2, 3, 3, 1, 0, 2, 1, 0, 2, 3, 1, 3, 0, 2, 1, 0, 2, 3, 3, 1, 0, 2, 0, 2, 1, 3, 1, 3, 0, 2, 0, 2, 1, 3, 3, 1, 0, 2, 2, 0, 1, 3, 1, 3, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 0, 3, 1, 2, 1, 3, 0, 2, 0, 3, 1, 2, 3, 1, 0, 2, 3, 0, 1, 2, 1, 3, 0, 2, 3, 0, 1, 2, 3, 1, 0, 2, 1, 2, 0, 3, 1, 3, 0, 2, 1, 2, 0, 3, 3, 1, 0, 2, 2, 1, 0, 3, 1, 3, 0, 2, 2, 1, 0, 3, 3, 1, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 3, 1, 0, 2, 1, 3, 0, 2, 3, 1, 0, 2, 3, 1, 0, 2, 2, 3, 0, 1, 1, 3, 0, 2, 2, 3, 0, 1, 3, 1, 0, 2, 3, 2, 0, 1, 1, 3, 0, 2, 3, 2, 0, 1, 3, 1, 0, 3, 0, 1, 2, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 2, 0, 3, 1, 0, 2, 3, 2, 1, 0, 3, 1, 0, 2, 3, 1, 2, 0, 3, 0, 2, 1, 3, 2, 1, 0, 3, 0, 2, 1, 3, 1, 2, 0, 3, 2, 0, 1, 3, 2, 1, 0, 3, 2, 0, 1, 3, 1, 2, 0, 3, 0, 3, 1, 2, 2, 1, 0, 3, 3, 0, 1, 2, 2, 1, 0, 3, 1, 2, 0, 3, 2, 1, 0, 3, 1, 2, 0, 3, 1, 2, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 1, 2, 0, 3, 1, 3, 0, 2, 2, 1, 0, 3, 1, 3, 0, 2, 1, 2, 0, 3, 3, 1, 0, 2, 2, 1, 0, 3, 3, 1, 0, 2, 1, 2, 0, 3, 2, 3, 0, 1, 2, 1, 0, 3, 2, 3, 0, 1, 1, 2, 0, 3, 3, 2, 0, 1, 2, 1, 0, 3, 3, 2, 0, 1, 1, 2, 1, 2, 0, 1, 2, 3, 0, 3, 1, 2, 0, 1, 2, 3, 3, 0, 1, 2, 1, 0, 2, 3, 0, 3, 1, 2, 1, 0, 2, 3, 3, 0, 1, 2, 0, 2, 1, 3, 0, 3, 1, 2, 0, 2, 1, 3, 3, 0, 1, 2, 2, 0, 1, 3, 0, 3, 1, 2, 2, 0, 1, 3, 3, 0, 1, 2, 0, 3, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 3, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 1, 2, 0, 3, 0, 3, 1, 2, 1, 2, 0, 3, 3, 0, 1, 2, 2, 1, 0, 3, 0, 3, 1, 2, 2, 1, 0, 3, 3, 0, 1, 2, 1, 3, 0, 2, 3, 0, 1, 2, 3, 1, 0, 2, 0, 3, 1, 2, 3, 1, 0, 2, 3, 0, 1, 2, 2, 3, 0, 1, 0, 3, 1, 2, 2, 3, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 0, 3, 1, 2, 3, 2, 0, 1, 3, 0, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 0, 2, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 0, 3, 1, 2, 0, 2, 1, 3, 0, 3, 1, 2, 2, 0, 1, 3, 3, 0, 1, 2, 0, 2, 1, 3, 3, 0, 1, 2, 2, 0, 1, 3, 1, 2, 0, 3, 0, 2, 1, 3, 1, 2, 0, 3, 2, 0, 1, 3, 2, 1, 0, 3, 0, 2, 1, 3, 2, 1, 0, 3, 2, 0, 1, 3, 1, 3, 0, 2, 0, 2, 1, 3, 1, 3, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 0, 2, 1, 3, 3, 1, 0, 2, 2, 0, 1, 3, 2, 3, 0, 1, 2, 0, 1, 3, 3, 2, 0, 1, 0, 2, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 0, 2, 3, 1, 0, 2, 3, 1, 0, 2, 3, 0, 2, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 2, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 3, 1, 0, 2, 3, 0, 3, 1, 2, 0, 1, 2, 3, 0, 3, 1, 2, 1, 0, 2, 3, 3, 0, 1, 2, 0, 1, 2, 3, 3, 0, 1, 2, 1, 0, 2, 3, 1, 2, 0, 3, 0, 1, 2, 3, 1, 2, 0, 3, 1, 0, 2, 3, 2, 1, 0, 3, 0, 1, 2, 3, 2, 1, 0, 3, 1, 0, 2, 3, 1, 3, 0, 2, 0, 1, 2, 3, 1, 3, 0, 2, 1, 0, 2, 3, 3, 1, 0, 2, 1, 0, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 1, 0, 2, 3, 3, 2, 0, 1, 1, 0 ] def rotate_left(n, d): d &= 0xF return (((n << d) & INT_SIZE) | (n >> (INT_BITS - d))) def rotate_right(n, d): d &= 0xF return (((n >> d) & INT_SIZE) | (n << (INT_BITS - d))) def mul(a, b): if a == 0: a = 0x10000 if b == 0: b = 0x10000 return (((a * b) % 0x10001) & INT_SIZE) def inv_mul(a): # calc multiplicative inverse with fermat's little theorem return pow(a, 65535, 0x10001) class NEAT(): round_keys = [] round_xcons = [] dec_round_keys = [] dec_round_xcons = [] def __init__(self, key): self.key_schedule(key) self.dekey_schedule() def f_function(self, x, k): x[1] ^= x[0] x[2] ^= x[3] x[0] = (x[0]+x[2]) & INT_SIZE x[3] = (x[3]+x[1]) & INT_SIZE x[1] = mul(x[1], k[0]) x[2] = mul(x[2], k[1]) x[0] = rotate_left(x[0], x[1]) x[3] = rotate_left(x[3], x[2]) x[1] = (x[1] + x[3]) & INT_SIZE x[2] = (x[2] + x[0]) & INT_SIZE def inv_f_function(self, x, k): x[1] = (x[1] - x[3]) & INT_SIZE x[2] = (x[2] - x[0]) & INT_SIZE x[0] = rotate_right(x[0], x[1]) x[3] = rotate_right(x[3], x[2]) x[1] = mul(x[1], k[2]) x[2] = mul(x[2], k[3]) x[0] = (x[0] - x[2]) & INT_SIZE x[3] = (x[3] - x[1]) & INT_SIZE x[1] ^= x[0] x[2] ^= x[3] def xor_layer(self, x, xcon): x[xcon[0]] ^= x[xcon[2] + 4] x[xcon[1]] ^= x[xcon[3] + 4] x[xcon[4] + 4] ^= x[xcon[6]] x[xcon[5] + 4] ^= x[xcon[7]] def round_function(self, x, k, xcon): l, r = x[:4], x[4:] self.f_function(l, k) self.inv_f_function(r, k) x = l + r self.xor_layer(x, xcon) return x def key_schedule(self, key): x = [] rk = [] # block to uint16 for i in range(0, 16, 2): x.append(key[i] << 8 | key[i+1]) # generate 13 round keys for i in range(7): x = self.round_function(x, key_const, fixed_xcon) rk.append(x[:4]) rk.append(x[4:]) self.round_keys = rk[:13] # generate 12 round xcon for i in range(12): pos = ((rk[i][0] ^ rk[i][3]) & 0x7F) * 8 xcon = xbox[pos:pos + 8] self.round_xcons.append(xcon) def dekey_schedule(self): # generate 13 dec round keys rev_rk = self.round_keys[::-1] for i in range(13): dec_rk = inv_mul(rev_rk[i][2]), inv_mul(rev_rk[i][3]), inv_mul(rev_rk[i][0]), inv_mul(rev_rk[i][1]) self.dec_round_keys.append(list(dec_rk)) # generate 12 dec round xcon rk = self.round_keys for i in range(12): pos = ((rk[i][0] ^ rk[i][3]) & 0x7F) * 8 xcon = xbox[pos:pos + 8] xcon[:4], xcon[4:] = xcon[4:], xcon[:4] self.dec_round_xcons.append(xcon) self.dec_round_xcons = self.dec_round_xcons[::-1] def encrypt(self, in_block): rk = self.round_keys rx = self.round_xcons out_block = self.neat_crypt(in_block, rk, rx) return out_block def decrypt(self, in_block): rk = self.dec_round_keys rx = self.dec_round_xcons out_block = self.neat_crypt(in_block, rk, rx) return out_block def neat_crypt(self, in_block, rk, rx): x = [] out_block = [] # block to uint16 for i in range(0, 16, 2): x.append(in_block[i] | (in_block[i+1] << 8)) # 12 round function for i in range(12): x = self.round_function(x, rk[i], rx[i]) # half round l, r = x[:4], x[4:] self.f_function(l, rk[12]) self.inv_f_function(r, rk[12]) x = l + r # final swap x[:4], x[4:] = x[4:], x[:4] # uint16 to block for i in range(0, 8): out_block.append(x[i] & 0xFF) out_block.append(x[i] >> 8) return bytes(out_block) def main(): print("[*] Preprocess") # key = bytes.fromhex("4142434445464748494A4B4C4D4E4F50") key = b'ABCDEFGHIJKLMNOP' print("[*] key:", key) neat = NEAT(key) plaintext = b'M4KE_NE4T_NE4TER' print("[*] plaintext:", plaintext) print("\n[*] Encrypt") ciphertext = neat.encrypt(plaintext) print("[*] ciphertext:", ciphertext) print("\n[*] Decrypt") recovered = neat.decrypt(ciphertext) print("[*] recovered:", recovered) if __name__ == '__main__': main() # EOF
NEAT - C
Makefile
CC = gcc CFlAGS = -W -Wall -o2 N = neat all: $N $N: $N.c @echo "##### [*] build neat" $(CC) $(CFLAGS) -o $@ $^ clean: @rm -rf *.o # EOF
neat.c
#include <stdio.h> #include <stdint.h> #include <string.h> #define SWAP(A, B) {uint16_t T; T = A; A = B; B = T;} typedef struct neat_key_struct_s { uint16_t enc_round_keys[13][4]; uint16_t dec_round_keys[13][4]; uint8_t enc_round_xcons[12][8]; uint8_t dec_round_xcons[12][8]; } neat_key_struct_t; const uint16_t key_const[] = { 0xFD56, 0xC7D1, 0xE36C, 0xA2DC }; const uint8_t fixed_xcon[] = { 0, 1, 0, 1, 2, 3, 2, 3 }; const uint8_t xbox[] = { 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 3, 2, 0, 1, 1, 0, 2, 3, 2, 3, 0, 1, 1, 0, 2, 3, 3, 2, 0, 1, 0, 2, 1, 3, 3, 2, 0, 1, 2, 0, 1, 3, 2, 3, 0, 1, 2, 0, 1, 3, 3, 2, 0, 1, 0, 3, 1, 2, 2, 3, 0, 1, 0, 3, 1, 2, 3, 2, 0, 1, 3, 0, 1, 2, 2, 3, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 1, 2, 0, 3, 2, 3, 0, 1, 1, 2, 0, 3, 3, 2, 0, 1, 2, 1, 0, 3, 2, 3, 0, 1, 2, 1, 0, 3, 3, 2, 0, 1, 1, 3, 0, 2, 2, 3, 0, 1, 1, 3, 0, 2, 3, 2, 0, 1, 3, 1, 0, 2, 2, 3, 0, 1, 3, 1, 0, 2, 3, 2, 0, 1, 2, 3, 0, 1, 3, 2, 0, 1, 3, 2, 0, 1, 2, 3, 0, 2, 0, 1, 2, 3, 3, 1, 0, 2, 1, 0, 2, 3, 1, 3, 0, 2, 1, 0, 2, 3, 3, 1, 0, 2, 0, 2, 1, 3, 1, 3, 0, 2, 0, 2, 1, 3, 3, 1, 0, 2, 2, 0, 1, 3, 1, 3, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 0, 3, 1, 2, 1, 3, 0, 2, 0, 3, 1, 2, 3, 1, 0, 2, 3, 0, 1, 2, 1, 3, 0, 2, 3, 0, 1, 2, 3, 1, 0, 2, 1, 2, 0, 3, 1, 3, 0, 2, 1, 2, 0, 3, 3, 1, 0, 2, 2, 1, 0, 3, 1, 3, 0, 2, 2, 1, 0, 3, 3, 1, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 3, 1, 0, 2, 1, 3, 0, 2, 3, 1, 0, 2, 3, 1, 0, 2, 2, 3, 0, 1, 1, 3, 0, 2, 2, 3, 0, 1, 3, 1, 0, 2, 3, 2, 0, 1, 1, 3, 0, 2, 3, 2, 0, 1, 3, 1, 0, 3, 0, 1, 2, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 2, 0, 3, 1, 0, 2, 3, 2, 1, 0, 3, 1, 0, 2, 3, 1, 2, 0, 3, 0, 2, 1, 3, 2, 1, 0, 3, 0, 2, 1, 3, 1, 2, 0, 3, 2, 0, 1, 3, 2, 1, 0, 3, 2, 0, 1, 3, 1, 2, 0, 3, 0, 3, 1, 2, 2, 1, 0, 3, 3, 0, 1, 2, 2, 1, 0, 3, 1, 2, 0, 3, 2, 1, 0, 3, 1, 2, 0, 3, 1, 2, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 1, 2, 0, 3, 1, 3, 0, 2, 2, 1, 0, 3, 1, 3, 0, 2, 1, 2, 0, 3, 3, 1, 0, 2, 2, 1, 0, 3, 3, 1, 0, 2, 1, 2, 0, 3, 2, 3, 0, 1, 2, 1, 0, 3, 2, 3, 0, 1, 1, 2, 0, 3, 3, 2, 0, 1, 2, 1, 0, 3, 3, 2, 0, 1, 1, 2, 1, 2, 0, 1, 2, 3, 0, 3, 1, 2, 0, 1, 2, 3, 3, 0, 1, 2, 1, 0, 2, 3, 0, 3, 1, 2, 1, 0, 2, 3, 3, 0, 1, 2, 0, 2, 1, 3, 0, 3, 1, 2, 0, 2, 1, 3, 3, 0, 1, 2, 2, 0, 1, 3, 0, 3, 1, 2, 2, 0, 1, 3, 3, 0, 1, 2, 0, 3, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 3, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 1, 2, 0, 3, 0, 3, 1, 2, 1, 2, 0, 3, 3, 0, 1, 2, 2, 1, 0, 3, 0, 3, 1, 2, 2, 1, 0, 3, 3, 0, 1, 2, 1, 3, 0, 2, 3, 0, 1, 2, 3, 1, 0, 2, 0, 3, 1, 2, 3, 1, 0, 2, 3, 0, 1, 2, 2, 3, 0, 1, 0, 3, 1, 2, 2, 3, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 0, 3, 1, 2, 3, 2, 0, 1, 3, 0, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 0, 2, 1, 3, 2, 0, 1, 3, 2, 0, 1, 3, 0, 3, 1, 2, 0, 2, 1, 3, 0, 3, 1, 2, 2, 0, 1, 3, 3, 0, 1, 2, 0, 2, 1, 3, 3, 0, 1, 2, 2, 0, 1, 3, 1, 2, 0, 3, 0, 2, 1, 3, 1, 2, 0, 3, 2, 0, 1, 3, 2, 1, 0, 3, 0, 2, 1, 3, 2, 1, 0, 3, 2, 0, 1, 3, 1, 3, 0, 2, 0, 2, 1, 3, 1, 3, 0, 2, 2, 0, 1, 3, 3, 1, 0, 2, 0, 2, 1, 3, 3, 1, 0, 2, 2, 0, 1, 3, 2, 3, 0, 1, 2, 0, 1, 3, 3, 2, 0, 1, 0, 2, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 0, 2, 3, 1, 0, 2, 3, 1, 0, 2, 3, 0, 2, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 2, 0, 1, 3, 0, 1, 2, 3, 2, 0, 1, 3, 1, 0, 2, 3, 0, 3, 1, 2, 0, 1, 2, 3, 0, 3, 1, 2, 1, 0, 2, 3, 3, 0, 1, 2, 0, 1, 2, 3, 3, 0, 1, 2, 1, 0, 2, 3, 1, 2, 0, 3, 0, 1, 2, 3, 1, 2, 0, 3, 1, 0, 2, 3, 2, 1, 0, 3, 0, 1, 2, 3, 2, 1, 0, 3, 1, 0, 2, 3, 1, 3, 0, 2, 0, 1, 2, 3, 1, 3, 0, 2, 1, 0, 2, 3, 3, 1, 0, 2, 1, 0, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 1, 0, 2, 3, 3, 2, 0, 1, 1, 0 }; uint16_t mul(uint32_t a, uint32_t b) { if (a == 0) a = 0x10000; if (b == 0) b = 0x10000; return (a * b) % 0x10001; } uint16_t inv_mul(uint16_t a) { // calc multiplicative inverse with extended euclidean algorithm if (a <= 1) { return a; } int m = 0x10001, x = 0, x0 = 1, q, tmp; while (a) { q = m / a, tmp = x; x = x0, x0 = tmp - q * x0; tmp = m, m = a; a = tmp - q * a; } return (x + 0x10001) % 0x10001; } uint16_t rotate_left (uint16_t n, uint16_t d) { d &= 0xF; return (n << d) | (n >> (16 - d)); } uint16_t rotate_right (uint16_t n, uint16_t d) { d &= 0xF; return (n << (16 - d)) | (n >> d); } void f_function(uint16_t* x, const uint16_t* k) { x[1] ^= x[0]; x[2] ^= x[3]; x[0] += x[2]; x[3] += x[1]; x[1] = mul(x[1], k[0]); x[2] = mul(x[2], k[1]); x[0] = rotate_left(x[0], x[1]); x[3] = rotate_left(x[3], x[2]); x[1] += x[3]; x[2] += x[0]; } void inv_f_function(uint16_t* x, const uint16_t* k) { x[1] -= x[3]; x[2] -= x[0]; x[0] = rotate_right(x[0], x[1]); x[3] = rotate_right(x[3], x[2]); x[1] = mul(x[1], k[2]); x[2] = mul(x[2], k[3]); x[0] -= x[2]; x[3] -= x[1]; x[1] ^= x[0]; x[2] ^= x[3]; } void xor_layer(uint16_t* x, uint8_t* xcon) { x[xcon[0]] ^= x[xcon[2] + 4]; x[xcon[1]] ^= x[xcon[3] + 4]; x[xcon[4] + 4] ^= x[xcon[6]]; x[xcon[5] + 4] ^= x[xcon[7]]; } void round_function(uint16_t* x, uint16_t* rk, uint8_t* xcon) { f_function(x, rk); inv_f_function(x+4, rk); xor_layer(x, xcon); } void neat_enc_key_schedule(neat_key_struct_t* ks, uint8_t* key) { uint16_t x[8] = {0x0000, }; uint16_t pos = -1; uint8_t xcon[8] = {0x0, }; // block to uint16 for (int i = 0; i < 16; i += 2) x[i / 2] = key[i + 1] | (key[i] << 8); // generate 12 round keys for (int i = 0; i < 12; i += 2) { round_function(&x, &key_const, &fixed_xcon); memcpy(ks->enc_round_keys[i], x, 8); memcpy(ks->enc_round_keys[i + 1], x + 4, 8); } // generate 1 round keys round_function(&x, &key_const, &fixed_xcon); memcpy(ks->enc_round_keys[12], x, 8); // generate 12 round xcon for (int i = 0; i < 12; ++i) { pos = ((ks->enc_round_keys[i][0] ^ ks->enc_round_keys[i][3]) & 0x7F) << 3; memcpy(ks->enc_round_xcons[i], &xbox[pos], 16); } } void neat_dec_key_schedule(neat_key_struct_t* ks, uint8_t* key) { // generate 13 dec round keys for (int i = 0; i < 13; ++i) { ks->dec_round_keys[i][0] = inv_mul(ks->enc_round_keys[12 - i][2]); ks->dec_round_keys[i][1] = inv_mul(ks->enc_round_keys[12 - i][3]); ks->dec_round_keys[i][2] = inv_mul(ks->enc_round_keys[12 - i][0]); ks->dec_round_keys[i][3] = inv_mul(ks->enc_round_keys[12 - i][1]); } // generate 12 dec round xcon for (int i = 0; i < 12; ++i) { memcpy(ks->dec_round_xcons[i], ks->enc_round_xcons[11 - i] + 4, 4); memcpy(ks->dec_round_xcons[i] + 4, ks->enc_round_xcons[11 - i], 4); } } void neat_init_key_schedule(neat_key_struct_t* ks, uint8_t* key) { neat_enc_key_schedule(ks, key); neat_dec_key_schedule(ks, key); } void neat_crypt(uint8_t* xb, uint16_t* rk, uint8_t* rx) { uint16_t x[8] = {0x0000, }; // block to uint16 for(int i = 0; i < 16; i += 2) x[i / 2] = xb[i] | (xb[i + 1] << 8); // 12 full rounds for(int i = 0; i < 12; ++i) { round_function(x, &rk[i*4], &rx[i*8]); } // half round f_function(&x[0], &rk[48]); inv_f_function(&x[4], &rk[48]); // final swap for (int i = 0; i < 4; ++i) { SWAP(x[i], x[i + 4]); } // convert back to bytes for (int i = 0; i < 16; i += 2) { xb[i] = x[i / 2]; xb[i + 1] = x[i / 2] >> 8; } } void neat_encrypt(uint8_t* xb, const neat_key_struct_t* ks) { neat_crypt(xb, &ks->enc_round_keys, &ks->enc_round_xcons); } void neat_decrypt(uint8_t* xb, const neat_key_struct_t* ks) { neat_crypt(xb, &ks->dec_round_keys, &ks->dec_round_xcons); } void show_as_hex(uint8_t* x) { for (int i = 0; i < 16; ++i) { printf("%02X", x[i]); } printf("\n"); } void show_as_str(uint8_t* x) { for (int i = 0; i < 16; ++i) { printf("%c", x[i]); } printf("\n"); } int main (int argc, char* argv[]) { uint8_t key[16] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50 }; uint8_t x[16] = { 0x4d, 0x34, 0x4b, 0x45, 0x5f, 0x4e, 0x45, 0x34, 0x54, 0x5f, 0x4e, 0x45, 0x34, 0x54, 0x45, 0x52 }; printf("[*] key:\t"); show_as_hex(&key); neat_key_struct_t ks; neat_init_key_schedule(&ks, key); printf("[*] plaintext:\t"); show_as_hex(&x); neat_encrypt(x, &ks); printf("[*] ciphertext:\t"); show_as_hex(&x); neat_decrypt(x, &ks); printf("[*] recovered:\t"); show_as_str(&x); } // EOF
Github
구현한 코드는 github에 공개한다.
Conclusion
[국정원의 국가기관용 비공개 암호 알고리즘 분석] 프로젝트 끝! 🧐
Uploaded by Notion2Tistory v1.1.0