Post

[암호학] 3. DES를 C언어로 구현해보았다.

[암호학] 3. DES를 C언어로 구현해보았다.

DES Code

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <stdio.h>
#include <string.h>
#include <stdint.h>

unsigned int IP[] = { ...
unsigned int inv_IP[] = { ...
unsigned int S[8][4][16] = {  ...
unsigned int E[] = {  ...
unsigned int P[] = {  ...
unsigned int PC1[] = {  ...
unsigned int PC2[] = {  ...

uint8_t get_bit(unsigned char *data, int n) {   //비트로 변환
    // n은 1 ~ 64
    int byte_index = (n - 1) / 8; // 몇 번째 바이트에 있는지 계산 (0 ~ 7)
    int bit_index = (n - 1) % 8;  // 그 바이트 내에서 몇 번째 비트인지 계산 (0 ~ 7)

    // 바이트 내에서는 왼쪽(MSB)부터 0번 인덱스 취급하므로 7에서 빼줍니다.
    return (data[byte_index] >> (7 - bit_index)) & 1;
}

void f_function(unsigned int *R, unsigned int *Key) {
    unsigned int ER[49];
    for (int i = 1; i < 49; i++){   //Expansion & Key XOR
        ER[i] = R[E[i-1]];
        ER[i] ^= Key[i];
    }
    unsigned int row;
    unsigned int column;
    for (int i = 0; i < 8; i++) //ER을 S박스에 넣어서 나온 값을 R에 넣기
    {
        row = (ER[i * 6 + 1] << 1) | ER[i * 6 + 6];
        column = (ER[i * 6 + 2] << 3) | (ER[i * 6 + 3] << 2) | (ER[i * 6 + 4] << 1) | ER[i * 6 + 5];
        R[i*4 + 1] = (S[i][row][column] >> 3) & 1; // 8의 자리
        R[i*4 + 2] = (S[i][row][column] >> 2) & 1; // 4의 자리
        R[i*4 + 3] = (S[i][row][column] >> 1) & 1; // 2의 자리
        R[i*4 + 4] = (S[i][row][column] >> 0) & 1; // 1의 자리
    }
    unsigned int Final_R[33];
    for (int i = 0; i < 32; i ++)
        Final_R[i+1] = R[P[i]];
    memcpy(R + 1, Final_R + 1, sizeof(unsigned int) * 32);  //코드 복사
};

void Key_Transform(unsigned int *C, unsigned int *D) {
    //Left Shift
    unsigned int num = C[1];
    memcpy(C + 1, C + 2, sizeof(unsigned int) * 27);
    C[28] = num;
    num = D[1];
    memcpy(D + 1, D + 2, sizeof(unsigned int) * 27);
    D[28] = num;
};

unsigned char plaintext[] = "QWERASDF";
unsigned char key_str[] = "KCHTBLOG";

int main() {
    unsigned int bitpt[65]; //평문 비트화
    unsigned int R[33]; //평문 R
    unsigned int L[33]; //평문 L
    for (int i = 1; i <= 64; i++)
        bitpt[i] = get_bit(plaintext, IP[i-1]);  //평문 비트화 하기, IP까지 한번에
    memcpy(L + 1, bitpt + 1, sizeof(unsigned int) * 32);
    memcpy(R + 1, bitpt + 33, sizeof(unsigned int) * 32);
    
    unsigned int Key[65];
    unsigned int C[29]; //Key C
    unsigned int D[29]; //Key D
    for (int i = 1; i <= 64; i++)
        Key[i] = get_bit(key_str, i);  //키 비트화 하기
    for (int i = 1; i <= 28; i++){
        C[i] = Key[PC1[i - 1]];
        D[i] = Key[PC1[i + 27]];
    }
    
    //round 1
    unsigned int Round_Key[49];
    unsigned int Out_R[33];
    unsigned int Out_L[33];

    for (int k = 1; k <= 16; k++){
        Key_Transform(C,D);     //transform
        if (k != 1 && k != 2 && k != 9 && k != 16) //이 때는 2번이므로
            Key_Transform(C,D);     //transform
        memcpy(Key + 1, C + 1, sizeof(unsigned int) * 28);
        memcpy(Key + 29, D + 1, sizeof(unsigned int) * 28);
        for (int i = 1; i <= 48; i++)   //라운드 키 생성
            Round_Key[i] = Key[PC2[i-1]];   //PC22
        memcpy(Out_L + 1, R + 1, sizeof(unsigned int) * 32);    //Out_L 만들기
        f_function(R, Round_Key);   //R에 f-function 적용
        for (int i = 1; i <= 32; i++)     //Out_R 만들기
            Out_R[i] = L[i] ^ R[i];
        memcpy(R + 1, Out_R + 1, sizeof(unsigned int) * 32);    //R에 Out_R 대입 (f_function 반복사용하기 위함)
        memcpy(L + 1, Out_L + 1, sizeof(unsigned int) * 32);    //L에 Out_L 대입 (f_function 반복사용하기 위함)
    }
    memcpy(bitpt + 1, R + 1, sizeof(unsigned int) * 32);    //마지막에 한번 더 전치를 하기 때문에 R이 먼저
    memcpy(bitpt + 33, L + 1, sizeof(unsigned int) * 32);
    unsigned int bitct[65]; //암호문
    for (int i = 1; i <= 64; i++)  //inv_IP
        bitct[i] = bitpt[inv_IP[i-1]];
    
    printf("Ciphertext (HEX): ");
    for (int i = 0; i < 8; i++) {
        unsigned int byte = 0;
        for (int j = 1; j <= 8; j++)
            byte = (byte << 1) | bitct[i * 8 + j];
        printf("%02X ", byte);
    }
    printf("\n");

    return 0;
}

지난번 포스팅에는 f함수까지 구현했는데 이번에는 라운드 적용부터 inv_IP까지 적용했다. 가장 실수를 많이 했던 부분은 다음과 같다.

1
2
3
4
5
6
7
8
//이 행렬들은 모두 원소가 1부터 시작한다.
unsigned int IP[] = { ...
unsigned int inv_IP[] = { ...
unsigned int S[8][4][16] = {  ...
unsigned int E[] = {  ...
unsigned int P[] = {  ...
unsigned int PC1[] = {  ...
unsigned int PC2[] = {  ...

C언어는 배열의 원소는 0부터 시작하는데, 구해온 DES의 행렬들은 원소가 1부터 시작한다. 따라서 코드의 index를 실수하지 않도록 신경써야 한다.

Desktop View DES에서 QWERASDFEDBKCHTBLOG라는 키를 이용하여 암호화 한 내용이 이와 같다.

Desktop View

github에서 내 코드를 실행한 것과 같은 값으로 암호화가 되었다.

This post is licensed under CC BY 4.0 by the author.