DES加密詳解

在下王建國發表於2021-03-02

注: 下述所有加法均為模二加, 即為亦或運算

1 根據輸入的祕鑰得到16個子祕鑰

1.1 大致流程

graph TB; a[輸入一個64位長度的字串,即為祕鑰K_0] b[根據PC-1表挑出56位作為新祕鑰K_1] c[K_1拆分,迴圈左移得到16個子祕鑰k0...k15] d[使用PC-2表從各子祕鑰56位中挑出48位] a-->b-->c-->d
圖1.1.1 演算法流程

image-20210301204737481

圖1.1.2 PC-1表及PC-2表

1.2 利用PC-1從K_0中挑出K_1

​ 接下來我們以輸入的祕鑰為"0x123456789abcdeff"為例, 及二進位制編碼為:

"0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111"

image-20210301210922245

圖1.2.1 初始祕鑰K_0

​ 接下來我們利用PC-1表生成新祕鑰K_1

image-20210301211558977

圖1.2.2 挑出的56位二進位制串

"1111 0000 1100 1100 1010 1010 1111"和"1101 0101 1110 0110 1111 1000 1111"

兩邊各是28位, 共56位

1.3 利用PC-2從K_1中挑出16個子祕鑰

​ 對於從1.2得來的一串56位二進位制碼, 我們將其拆分成兩串28位二進位制碼, 即為:

左半部分的"1111 0000 1100 1100 1010 1010 1111", 並將其命名為C_0

右半部分的"1101 0101 1110 0110 1111 1000 1111", 並將其命名為D_0

​ 接下來, 我們利用下表, 進行迴圈左移, 規則如下:

image-20210301230226344

圖1.3.1 位移規則表

​ 之後我們按照上面的規則, 展示子祕鑰k_1是如何生成的:

  1. 將C_0和D_0分別迴圈左移1位, 得到C_1-"1110 0001 1001 1001 0101 0101 1111"以及D_1-"1010 1011 1100 1101 1111 0001 1111"(PS:迴圈左移1位就是將首位移動到最後末尾)
  2. 將C_1即D_1拼接起來, 得到"1110 0001 1001 1001 0101 0101 1111 1010 1011 1100 1101 1111 0001 1111"
  3. 利用PC-2表從拼接得到的字串中挑選出子祕鑰k_1, 類似於"利用PC-1表的過程"

image-20210301233109216

圖1.3.2 從C_0和D_0中生成的子祕鑰k_0
  1. 所以子祕鑰k_1為"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"

​ 按照同樣的邏輯, 在C_0和D_0已被迴圈左移的基礎上, 重複上述步驟, 我們得到如下16個子祕鑰:

image-20210302004853659

圖1.3.3 生成的所有子祕鑰

PS: 上述輸出為自己寫的程式碼, 在文末會進行展示.

​ 得到了這16個子祕鑰後, 我們就可以對明文進行加密了, 我們繼續!

2 利用16個子祕鑰對明文進行加密

2.1 大致流程

graph TB; a[對64位的明文進行初始變化,簡稱IP-Initial Permutation] b[將IP後的明文平分成32位的L0-左半邊和R0-右半邊] c[接下來使用如下公式對L_n和R_n進行變換] d["1.L_n=R_n-1<br>2.R_n=L_n-1 + f(R_n-1, k_n)"] e["f的具體過程如下<br>1.將R_n-1擴充套件成48位並與k_n-1相加"得到S盒<br>2.使用8張S表將48位的S盒運算成32位輸出P<br>3.使用P表對輸出P進行位置變換] f["得到輸出L_16和R_16, 拼接成R_16L_16的64位輸出"] g["對這64位輸出使用IP-1表進行位置變換, 即得到最終輸出"] a-->b-->c-->d-.-e-.->f-->g d--重複16次-->d
圖2.1.1 演算法流程

2.2 將R_0擴充套件成48位的二進位制串, 並與子祕鑰k模二加

​ 我們以輸入的明文為"0x0123456789abcdef"為例, 即二進位制編碼為:

"0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111"

經過變換後,得到:

"1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1110 1111 0000 1010 1010"

將其拆分為L_0及R_0, 各32位

L_0: "1100 1100 0000 0000 1100 1100 1111 1111"

R_0: "1111 0000 1010 1110 1111 0000 1010 1010"

對R_0使用如下規則擴充套件成48位:

image-20210302212306838

圖2.2.1 R_0擴充套件成48位規則

PS: 綠色的標出的位數即為擴充套件出的12位

因此, 得到RExtend(8個6位): "011110 100001 010101 011101 011110 100001 010101 010101"

將其與子祕鑰"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"

亦或得S盒"0110 0001 0001 0111 1011 0010 1000 0110 0110 1100 0010 0011"

2.3 使用S1-S8表將48位變回32位

​ 為什麼上面S盒要備註8個6位呢? 原因就是要以8個6位為輸入, 分別經過S1-S8盒的運算,得到8個4位輸出, 也就變成了32位.

image-20210302152840419

圖2.3.1 S1-S8表

我們的S盒長這樣"011000 010001 011110 110010 100001 100110 110000 100011"

以第一個6位"011000"為輸入, 代入到S1表變換

"011000"的首位和末尾拼起來的數字為行號, 中間4位為列號(當然都是二進位制的), 即第0(00b)行, 第15(1100b)列, 查詢S1表, 為S1(0, 12) = 5 = 0101b

這樣就可以得到第一個4位的輸出, 按照這樣的方式可以得到8個4位的輸出, 合在一起就是一個32位的輸出, 經過運算是"0101 1100 1000 0001 1011 0101 1010 0001"

2.4 使用P表對32位輸出進行位置變換, 並和L0相加

​ 我們已經得到經過"擴位"和"縮位"的變換, 現在要按照P表進行簡單的位置變換:

image-20210302154240976

圖2.4.1 P表

image-20210302223935952

圖2.4.2 32位輸出的每一位情況

照著P表生成新的32位輸出"1010 0001 0000 1000 1010 1101 1001 1011"

再將輸出與L_0相加, L_0:"1100 1100 0000 0000 1100 1100 1111 1111"

得到結果: "0110 1101 0000 1000 0110 0001 0110 0100"

2.5 往復這樣的步驟16次, 將得到的R_16和L_16拼接並經IP^-1變換

​ 如題, 得到最終結果:

"1110 1100 1010 1001 0011 1101 1001 1100 1110 0100 1101 1001 1001 1001 1100 1110"

3 程式碼實現

/*
 * @Description: 
 * @Autor: Shmily
 * @Date: 2021-03-01 23:40:00
 * @LastEditTime: 2021-03-02 22:35:49
 */

#include <bits/stdc++.h>
using namespace std;

const int8_t PC_1[56] = {
  57, 49, 41, 33, 25, 17, 9, 1,
  58, 50, 42, 34, 26, 18, 10, 2,
  59, 51, 43, 35, 28, 19, 11, 3,
  60, 52, 44, 36,
  63, 55, 47, 39, 31, 23, 15, 7,
  62, 54, 46, 38, 30, 22, 14, 6,
  61, 53, 45, 37, 29, 21, 13, 5,
  28, 20, 12, 4 
};

const int8_t PC_2[48] = {
  14, 17, 11, 24, 1, 5,
  3, 28, 15, 6, 21, 10,
  23, 19, 12, 4, 26, 8,
  16, 7, 27, 20, 13, 2,
  41, 52, 31, 37, 47, 55,
  30, 40, 51, 45, 33, 48,
  44, 49, 39, 56, 34, 53,
  46, 42, 50, 36, 29, 32
};

const int8_t left_move[16] = {
  1, 1, 2, 2, 2, 2, 2, 2,
  1, 2, 2, 2, 2, 2, 2, 1
};

const int8_t S8[8][4][16] = {
  // S1
  {
    {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}
  },
  // S2
  {
    {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}
  },
  // S3
  {
    {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}
  },
  // S4
  {
    {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}
  },
  // S5
  {
    {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}
  },
  // S6
  {
    {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}
  },
  // S7
  {
    {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}
  },
  // S8
  {
    {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}
  }
};

const int8_t PTable[32] = {
  16, 7, 20, 21,
  29, 12, 28, 17,
  1, 15, 23, 26,
  5, 18, 31, 10,
  2, 8, 24, 14,
  32, 27, 3, 9,
  19, 13, 30, 4,
  22, 11, 4, 25
};

const int8_t IP[2][64] = {
  {
    58, 50, 42, 34, 26, 18, 10, 2,
    60, 52, 44, 36, 28, 20, 12, 4,
    62, 54, 46, 38, 30, 22, 14, 6,
    64, 56, 48, 40, 32, 24, 16, 8,
    57, 49, 41, 33, 25, 17, 9, 1,
    59, 51, 43, 35, 27, 16, 11, 3,
    61, 53, 45, 37, 29, 21, 13, 5,
    63, 55, 47, 39, 31, 23, 15, 7
  },
  {
    40, 8, 48, 16, 56, 24, 64, 32,
    39, 7, 47, 15, 55, 23, 63, 31,
    38, 6, 46, 14, 54, 22, 62, 30,
    37, 5, 45, 13, 53, 21, 61, 29,
    36, 4, 44, 12, 52, 20, 60, 28,
    35, 3, 43, 11, 51, 19, 59, 27,
    34, 2, 42, 10, 50, 18, 58, 26,
    33, 1, 41, 9, 49, 17, 57, 25
  }
};

void out_int8_t(int8_t* out, int len) {
  for (int i = 0; i < len; i++) {
    printf("%d", out[i]);
    if ((i + 1) % 4 == 0) cout << " ";
  }
  cout << endl;
}

void fun_left_move(int8_t* C_0, int8_t* D_0, int times_left) {
  for (int i = 0; i < times_left; i++) {
    int8_t temp1 = C_0[0];
    int8_t temp2 = D_0[0];
    for (int j = 1; j < 28; j++) {
      C_0[j - 1] = C_0[j];
      D_0[j - 1] = D_0[j];
    }
    C_0[27] = temp1;
    D_0[27] = temp2;
  }
}

void Extend32To48(int8_t (*R) [32], int8_t (*RExtend) [48], int times) {
  RExtend[times][0] = R[times][31];
  RExtend[times][47] = R[times][0];
  for (int i = 1; i < 47; i++) {
    // 6個位一組, 即為一行
    int LineNumber = (int)(i / 6);
    RExtend[times][i] = R[times][i - 1 - LineNumber*2];
  }
}

void XOR(int8_t (*RExtend) [48], int8_t (*k) [48], int8_t (*S) [48], int times) {
  for (int i = 0; i < 48; i++) {
    // 進行模二加運算
    S[times][i] = (RExtend[times][i] + k[times][i]) % 2;
  }
}

void XOR(int8_t (*L) [32], int8_t (*Pchanged) [32], int8_t (*R) [32], int times) {
  for (int i = 0; i < 32; i++) {
    R[times + 1][i] = (L[times][i] + Pchanged[times][i]) % 2;
  }
}

void UseS48To32(int8_t (*S) [48], int8_t (*P) [32], int times) {
  for (int i = 0; i < 8; i++) {
    int line = 0, row = 0;
    // 計算出行號及列號
    line = line + S[times][i*6]*2 + S[times][5 + i*6];
    row = row + S[times][1 + i*6]*8 + S[times][2 + i*6]*4 + S[times][3 + i*6]*2 + S[times][4 + i*6];
    // 從S表中挑出答案
    int8_t ans = S8[i][line][row];
    for (int j = 0; j < 4; j++) {
      int subtrahend = pow(2, (3 - j));
      if (ans - subtrahend >= 0) {
        P[times][j + i*4] = 1;
        ans -= subtrahend;
      }
      else P[times][j + i*4] = 0;
    }
  }
}

void UsePChangeP(int8_t (*P) [32], int8_t (*PChanged) [32], int times) {
  for (int i = 0; i < 32; i++) {
    PChanged[times][i] = P[times][PTable[i] - 1];
  }
}

void UseIPChange(int8_t* in, int8_t* out, int8_t choice) {
  for (int i = 0; i < 64; i++) {
    out[i] = in[IP[choice][i] - 1];
  }
}

int main(void) {
  // 對祕鑰進行處理
  // 輸入為64位祕鑰, 輸出位16個32位子祕鑰
  int8_t K_0[64], K_1[56], k[16][48];
  // 祕鑰為"0x123456789abcdeff"
  char in[] = "0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111";
  int len = strlen(in);
  // 將輸入的祕鑰儲存到K_0中
  for (int i = 0, j = 0; i < len; i++) {
    if (in[i] == '0') K_0[j++] = 0;
    else if (in[i] == '1') K_0[j++] = 1;
  }
  // 利用PC-0表, 從K_0中取出K_1
  for (int i = 0; i < 56; i++) {
    // 注意-1
    K_1[i] = K_0[PC_1[i]-1];
  }
  // 上述完成了第一步利用PC-1表
  // 接下來利用PC-2表
  int8_t C_0[28], D_0[28];
  // 將K_1拆分
  for (int i = 0; i < 56; i++) {
    if (i < 28) C_0[i] = K_1[i];
    else D_0[i - 28] = K_1[i];
  }
  // 迴圈16次生成16個子祕鑰
  for (int i = 0; i < 16; i++) {
    int8_t times_left = left_move[i];
    fun_left_move(C_0, D_0, times_left);
    for (int j = 0; j < 48; j++) {
      int8_t sub = PC_2[j] - 1;
      if (sub < 28) k[i][j] = C_0[sub];
      else k[i][j] = D_0[sub - 28];
    }
    // printf("子祕鑰k[%.2d]: ", i + 1);
    // out_int8_t(k[i], 48);
  }

  
  // 對明文進行處理
  // 明文為"0x0123456789abcdef"
  char in_text[] = "0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111";
  int8_t PlainText[64], IP_PlainText[64], EnchiperTextInitial[64], EnchiperText[64];
  int8_t L[17][32], R[17][32], RExtend[16][48], S[16][48], P[16][32], PChanged[16][32];
  int len_text = strlen(in_text);
  // 64位明文儲存到PlainText中
  for (int i = 0, j = 0; i < len; i++) {
    if (in_text[i] == '0') PlainText[j++] = 0;
    else if (in_text[i] == '1') PlainText[j++] = 1;
  }
  // 使用IP對PlainText進行變換
  UseIPChange(PlainText, IP_PlainText, 0);
  // 將PlainText分左右儲存到L_0及R_0中
  for (int i = 0; i < 64; i++) {
    if (i < 32) L[0][i] = IP_PlainText[i];
    else R[0][i - 32] = IP_PlainText[i];
  }
  // 迴圈16次
  for (int times = 0; times < 16; times++) {
    // L_n = R_n-1
    for (int i = 0; i < 32; i++) {
      L[times + 1][i] = R[times][i];
    }
    
    // L_n = L_n-1 + f(R_n-1, k_n)
    // 對R進行擴充套件
    Extend32To48(R, RExtend, times);
 
    // 將RExtend與k_n-1模二加
    XOR(RExtend, k, S, times);

    // 使用S表將S盒48位壓縮成32位
    UseS48To32(S, P, times);

    // 使用P表對P進行位置變換
    UsePChangeP(P, PChanged, times);
    
    // 生成的PChanged就是經過f之後的
    // 模二加後放入R_n
    XOR(L, PChanged, R, times);
  }

  for (int i = 0; i < 64; i++) {
    if (i < 32) EnchiperTextInitial[i] = R[16][i];
    else EnchiperTextInitial[i] = L[16][i - 32];
  }
  // 使用IP^-1對EnchiperTextInitial的進行變換得到Enchiper
  UseIPChange(EnchiperTextInitial, EnchiperText, 1);

  //最終結果輸出
  out_int8_t(EnchiperText, 64);

  return 0;
}

相關文章