본문 바로가기

학업 정리

DES 구현하기

얼마 전 강의에서 DES를 통한 암호화에 대해 배웠다. 이것을 코드로 구현하면 어떨까 해서 한 번 작성해보기로 했다.

나중에 웹서비스로 확장시키면 좋을 것 같아서 언어는 자바스크립트(Javascript, ES6)를 택했다.

 

  • DES를 쉽게 구현하기 위해 permutation, xor, shift, sBox 등의 함수를 먼저 구현했다.
  • permutation table은 강의 내용을 참고했다.
  • 실제 DES와 결과가 같은지는 아직 모르겠다. DES 구조에 대한 강의 슬라이드 중 마지막 부분에 32-bit Swap이라 적힌 단계가 있는데 이것에 대해서는 언급되지 않았다. 그래서 Round 1 ~ Round 16과 IP에 집중하기로 했다.
  • 데이터 형식은 binary로 하려고 했지만 웹서비스로의 확장 등에는 '0'과 '1'로 이루어진 문자열이 편할 것 같았다. 그래서 일단 문자열을 다루도록 했다.
  • DES 함수의 매개변수인 plaintext와 key는 길이가 64여야 한다.
/*

	DES code by gunhoflash

*/


// Initial Permutation
const IP_TABLE = [
	58, 50, 42, 34, 26, 18, 10, 02,
	60, 52, 44, 36, 28, 20, 12, 04,
	62, 54, 46, 38, 30, 22, 14, 06,
	64, 56, 48, 40, 32, 24, 16, 08,
	57, 49, 41, 33, 25, 17, 09, 01,
	59, 51, 43, 35, 27, 19, 11, 03,
	61, 53, 45, 37, 29, 21, 13, 05,
	63, 55, 47, 39, 31, 23, 15, 07
];

// Inverse of Initial Permutation
const IIP_TABLE = [
	40, 08, 48, 16, 56, 24, 64, 32,
	39, 07, 47, 15, 55, 23, 63, 31,
	38, 06, 46, 14, 54, 22, 62, 30,
	37, 05, 45, 13, 53, 21, 61, 29,
	36, 04, 44, 12, 52, 20, 60, 28,
	35, 03, 43, 11, 51, 19, 59, 27,
	34, 02, 42, 10, 50, 18, 58, 26,
	33, 01, 41, 09, 49, 17, 57, 25
];

// Expansion Permutation
const EXPANSION_TABLE = [
	32, 01, 02, 03, 04, 05,
	04, 05, 06, 07, 08, 09,
	08, 09, 10, 11, 12, 13,
	12, 13, 14, 15, 16, 17,
	16, 17, 18, 19, 20, 21,
	20, 21, 22, 23, 24, 25,
	24, 25, 26, 27, 28, 29,
	28, 29, 30, 31, 32, 01
];

// S-box Substitution
const S_BOX = [
	[
		14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
		0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
		4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
		15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
	],
	[
		15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
		3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
		0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
		13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
	],
	[
		10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
		13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
		13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
		1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
	],
	[
		7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
		13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
		10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
		3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
	],
	[
		2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
		14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
		4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
		11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
	],
	[
		12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
		10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
		9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
		4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
	],
	[
		4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
		13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
		1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
		6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
	],
	[
		13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
		1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
		7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
		2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
	]
];

// P-box Permutation
const P_BOX_TABLE = [
	16, 07, 20, 21, 29, 12, 28, 17, 01, 15, 23, 26, 05, 18, 31, 10,
	02, 08, 24, 14, 32, 27, 03, 09, 19, 13, 30, 06, 22, 11, 04, 25
];

// Initial Key Selection
const INITIAL_KEY_TABLE = [
	57, 49, 41, 33, 25 ,17, 09,
	01, 58, 50, 42, 34, 26, 18,
	10, 02, 59, 51, 43, 35, 27,
	19, 11, 03, 60, 52, 44, 36,
	63, 55, 47, 39, 31, 23, 15,
	07, 62, 54, 46, 38, 30, 22,
	14, 06, 61, 53, 45, 37, 29,
	21, 13, 05, 28, 20, 12, 04
];

// Key Permutation and Compression
const KEY_COMPRESSION_TABLE = [
	14, 17, 11, 24, 01, 05, 03, 28, 15, 06, 21, 10,
	23, 19, 12, 04, 26, 08, 16, 07, 27, 20, 13, 02,
	41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
	44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
];

// Shift Schedule of Keys
const SHIFT_SCHEDULE = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2 ,2, 1];

/*
	(not used) Return a bit string

	example:
		getBitString([], 4) = "0000"
		getBitString([1, 3, 7], 8) = "10100010"
		getBitString([1, 3, 4, 60]) = "1011000...00010000"
*/
getBitString = (ones, bit = 64) => {
	let i = 1, returns = "";
	ones.forEach(one => {
		while (i++ < one)
			returns += '0';
		returns += '1';
	});
	while (i++ <= bit)
		returns += '0';
	return returns;
};

/*
	Return a permutation of a given bit string

	example:
		permutation("0111", [4, 2, 1, 3]) = "1101"
		permutation("01100001", [1, 3, 5, 7, 2, 4, 6, 8]) = "01001001"
*/
permutation = (bitString, permutationTable) => {
	let i, returns = "";
	for (i = 0; i < permutationTable.length; i++)
		returns += bitString[permutationTable[i] - 1];
	return returns;
};

/*
	Return a result of the xor of two given bit strings

	example:
		xor("0111", "1001") = "1110"
		xor("01001011", "11000011") = "10001000"
*/
xor = (bitString1, bitString2) => {
	if (bitString1.length != bitString2.length)
	{
		console.log(`Error at xor: The length of both bitString is different`);
		return;
	}
	let i, returns = "";
	for (i = 0; i < bitString1.length; i++)
		returns += (bitString1[i] ^ bitString2[i]).toString();
	return returns;
};

/*
	Return a result of the shift of a given bit string

	example:
		leftShift("0111", 1) = "1110"
		leftShift("01001000", 3) = "01000010"
*/
leftShift = (bitString, number) => {
	if (!bitString.length)
	{
		console.log(`Error at leftShift: bitString is invalid`);
		return;
	}
	number %= bitString.length;
	return bitString.substr(number) + bitString.substr(0, number);
};

/*
	Return a result of the s-box of a given bit string
*/
sBox = (bitString) => {
	if (bitString.length != 48)
	{
		console.log(`Error at sBox: bitString.length (${bitString.length}) isn't 48`);
		return;
	}
	let i, bits, returns = "";
	for (i = 0; i < 8; i++)
	{
		bits = bitString.substr(i * 6, 6);
		returns += ("000" + (S_BOX[i][
			bits[0] * 32 +
			bits[5] * 16 +
			bits[1] * 8 +
			bits[2] * 4 +
			bits[3] * 2 +
			bits[4] * 1
		]).toString(2)).substr(-4);
	}
	return returns;
};

/*
	Return a result of DES of given bit string and key
*/
DES = (plaintext, key) => {
	console.log(`Start DES with\n\tplaintext: ${plaintext}\n\tkey: ${key}`);

	if (plaintext.length != 64)
	{
		console.log(`Error at DES: plaintext.length (${plaintext.length}) isn't 64`);
		return;
	}
	if (key.length != 64)
	{
		console.log(`Error at DES: key.length (${key.length}) isn't 64`);
		return;
	}
	
	let left, right, nextLeft;
	let leftKey, rightKey;
	let subKey;
	let round;

	// Initializing before the first round
	plaintext = permutation(plaintext, IP_TABLE);
	left = plaintext.substr(0, 32);
	right = plaintext.substr(32);
	nextLeft = right;

	subKey = permutation(key, INITIAL_KEY_TABLE);
	leftKey = subKey.substr(0, 28);
	rightKey = subKey.substr(28);

	for (round = 0; round < 16; round++)
	{
		console.log(`Start round ${round} with\n\tleft: ${left}\n\tright: ${right}`);
		
		// Get key
		leftKey = leftShift(leftKey, SHIFT_SCHEDULE[round]);
		rightKey = leftShift(rightKey, SHIFT_SCHEDULE[round]);
		subKey = permutation(leftKey + rightKey, KEY_COMPRESSION_TABLE);

		// E/P
		right = permutation(right, EXPANSION_TABLE);

		// XOR with Key
		right = xor(right, subKey);

		// S-box
		right = sBox(right);

		// P-box
		right = permutation(right, P_BOX_TABLE);

		// XOR
		right = xor(left, right);

		// End of a round
		left = nextLeft;
	}

	plaintext = permutation(right + left, IIP_TABLE);
	console.log(`End DES with\n\tencrypted: ${plaintext}`);
	return plaintext;
};

let plaintext = "0000000100100011010001010110011110001001101010111100110111101111";
let key = "0001001100110100010101110111100110011011101111001101111111110001";

DES(plaintext, key);

강의시간에 손으로 Round 1을 끝내는 것을 했는데, 이 코드를 통해서도 같은 결과가 나왔다.