Notice
Recent Posts
Recent Comments
«   2024/05   »
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
Tags
more
Archives
Today
Total
관리 메뉴

topcue

NEAT 암호 (5) 구현 본문

Crypto

NEAT 암호 (5) 구현

topcue 2021. 2. 8. 13:56

※ 본 프로젝트는 offensive research가 아닌 defensive 관점에서 진행한 학술적인 연구입니다.

✉ 윤형준 encrypt@kakao.com


[목차]


Overview

NEAT 암호 알고리즘 분석 프로젝트의 마지막 파트가 될 것 같다.

암호분석(cryptanalysis)을 안 할 수도 있고 혹시 하더라도 포스팅을 하지 않을 수도 있다.

앞서 분석한 NEAT를 C와 Python으로 구현하고 github에 공개한다.


Optimization

InvMul

modular 216+12^{16}+1에서 a1a^{-1}을 구하는 문제를 fertmat's little theorem을 이용해 제곱을 빠르게 구하는 문제로 치환하는 방법이다.

modular 216+12^{16}+1에서 aa의 역원을 구해야 한다.

소수 p=216+1p = 2^{16}+1라고 하자. 이때 ppn=2n=2인 fermat number다. (p=216+1=22n+1p = 2^{16}+1 = 2^{2^{n}}+1)

fermat's little theorem을 이용해 아래와 같이 전개할 수 있다.

a    ap1(modp)a \ \ \ \ \equiv a^{p-1} \pmod{p}

a1ap2                           (modp)       a2161                         (mod216+1)       a20+21+22++215         (mod216+1)       a(a(a(a2))2)2(mod216+1)a^{-1} \equiv a^{p-2}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \pmod{p} \\ \ \ \ \ \ \ \ \equiv a^{2^{16}-1}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \pmod{2^{16}+1} \\ \ \ \ \ \ \ \ \equiv a^{2^{0}+2^{1}+2^{2}+\cdots +2^{15}}\ \ \ \ \ \ \ \ \ \pmod{2^{16}+1} \\ \ \ \ \ \ \ \ \equiv a(a\cdots(a(a^2))^2\cdots)^2 \pmod{2^{16}+1}

하드웨어를 이용한다면 a(a(a(a2))2)2a(a\cdots(a(a^2))^2\cdots)^2와 같은 방법이 효율적이겠다.

파이썬 코드에서 벤치마킹한 결과 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에 공개한다.

topcue/neat-crypto
Contribute to topcue/neat-crypto development by creating an account on GitHub.
https://github.com/topcue/neat-crypto

Conclusion

[국정원의 국가기관용 비공개 암호 알고리즘 분석] 프로젝트 끝! 🧐


'Crypto' 카테고리의 다른 글

KCMVP 기초 암호수학  (0) 2021.03.04
NEAT 암호 (4) 동작 원리와 설계 사상  (0) 2021.02.08
NEAT 암호 (3) 알고리즘 분석  (0) 2021.02.08
NEAT 암호 (2) 리버싱  (0) 2021.02.08
NEAT 암호 (1) 개요  (0) 2021.02.08
Comments