얼마 전 강의에서 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을 끝내는 것을 했는데, 이 코드를 통해서도 같은 결과가 나왔다.